Document, comentariu, eseu, bacalaureat, liceu si facultate
Top documenteAdmitereTesteUtileContact
      
    


 


Ultimele referate adaugate

Adauga referat - poti sa ne ajuti cu un referat?

Politica de confidentialitate



Ultimele referate descarcare de pe site
  CREDITUL IPOTECAR PENTRU INVESTITII IMOBILIARE (economie)
  Comertul cu amanuntul (economie)
  IDENTIFICAREA CRIMINALISTICA (drept)
  Mecanismul motor, Biela, organe mobile proiect (diverse)
  O scrisoare pierduta (romana)
  O scrisoare pierduta (romana)
  Ion DRUTA (romana)
  COMPORTAMENT PROSOCIAL-COMPORTAMENT ANTISOCIAL (psihologie)
  COMPORTAMENT PROSOCIAL-COMPORTAMENT ANTISOCIAL (psihologie)
  Starea civila (geografie)
 

Ultimele referate cautate in site
   domnisoara hus
   legume
    istoria unui galban
   metanol
   recapitulare
   profitul
   caract
   comentariu liric
   radiolocatia
   praslea cel voinic si merele da aur
 
despre:
 
"TUTORUL" LUI C++ introducere in c++
Colt dreapta
Vizite: ? Nota: ? Ce reprezinta? Intrebari si raspunsuri
 
1.1 Introducere f9q4qm
-----------

1.1.1 Iesire
------

#include <stream.h> main()
A cout << "Hello, world\n";
S

#include <stream.h> - include declaratii pentru facilitatile standard de intrare/iesire aflate in stream.h.
Operatorul << scrie cel de al doilea operand al sau peste primul.

1.1.2 Compilare
Se apeleaza cu litere mari CC. Daca programul este in fisierul hello.c, atunci se compileaza si se executa ca mai jos:
$CC hello.c
$a.out
Hello, world
$

1.1.3 Intrare
#include <stream.h> main() //converteste inch in cm Aint inch = 0; cout << "inches"; cin >> inch; cout << inch; cout << "in="; cout << inch*2.54; cout << "cm\n";
S

Exemplu de executie
$a.out inches = 12
12 in = 30.48 cm
$

Ultimii 4 operatori pot fi scrisi: cout << inch << "in=" << inch*2.54 << "cm\n";

1.2 Comentariu
Incepe prin /* si se termina prin */.
Comentariu poate incepe prin // si se termina la sfirsitul liniei respective.

1.3 Tipuri si Declaratii
Fiecare nume si fiecare expresie are un tip care determina operatiile care pot fi facute asupra lor.
O declaratie este o instructiune care introduce un nume intr-un program. O declaratie specifica un tip pentru acel nume. Un tip defineste utilizarea numelui sau a unei expresii. Operatiile de forma +, -, * si / se definesc pentru intregi. Dupa ce s-a inclus stream.h, un int poate fi cel de al doilea operand pentru << cind primul argument este ostream.
Tipul unui obiect determina nu numai care operatii pot fi aplicate asupra lui, ci de asemenea intelesul acelor operatii. De exemplu, instructiunea: cout << inch << "in=" << inch*2.54 << "cm\n"; trateaza corect cele 4 valori de iesire care sint diferite.
C++ are citeva tipuri de baza si diferite moduri de a crea altele noi.




1.3.1 Tipuri fundamentale char short int long float double sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(float) <= sizeof(double) const float pi = 3.14; const char plus = '+';
Operatori aritmetici:
+ - (unari si binari ambii)
* / %
Operatori de comparare ca in C. double d = 1; int i = 1; d = d + i; i = d + i;

1.3.2 Tipuri derivate
* -> pointer
*const -> pointer constant
& -> adresa
ai -> vector
() -> functie

char* p; char *const q; char va10i; char c;
//...... p = &c; // p pointeaza spre c
1.4 Expresii si Instructiuni

I &(si) ^ | << >> se aplica la intregi
= op= x = sqrt (a = 3*x)
++ --

Cea mai frecventa forma a unei instructiuni este o instructiune expresie; ea consta dintr-o expresie urmata de un punct si virgula. a = b*3+c; cout << "go go go"; lseek(fd, 0, 2);

Instructiunea VIDA:
;

Blocuri:

A a = b + 2; b++;
S

Instructiunea IF:
#include <stream.h> main() //converteste din inch in cm si invers
A const float fac = 2.54; float x, in, cm; char ch = 0; cout << "enter lenght:"; cin >> x >> ch; if(ch=='i')
A //inch in = x; cm = x*fac;
S else if(ch=='c')
A //cm in = x/fac; cm = x;
S else in = cm = 0; cout << in << "in=" << cm << "cm\n";
S

Instructiunea SWITCH: switch(ch)
A case 'i': in = x; cm = x*fac; break; case 'c': in = x/fac; cm = x; break; default: in = cm = 0; break;
S

Instructiunea WHILE:
while(*p!=0)
A
*q = *p; q = q+1; p = p+1;
S
*q = 0;
while(*p)
*q++ = *p++;
*q = 0; while(*q++ = *p++);
Instructiunea FOR: for(int i=0; i<10; i++) qaii = paii;
Declaratii: for(int i=1; i<MAX; i++)
A int t = vai-1i; vai-1i = vaii; vaii = t;
S

1.5 Functii
-------

O functie este o parte denumita a programului care poate fi apelata din alte parti ale programului atit de des, cit este nevoie. extern float pow(float, int);
// pow este definita in alta parte main()
Afor(int i=0; i<10; i++) cout << pow(2, 1) << "\n"; pow(12.3, "abcd") //este eroare
S float pow(float x, int n)
A if(n<0) error("expresie negativ pentru pow"); switch(n)
A case 0: return 1; case 1: return x; default: return x*pow(x, n-1);
S
S

overload pow; int pow(int, int); double pow(double, double);
//....... x = pow(2, 10); y = pow(2.0, 10.0);

Declaratia overload pow informeaza compilatorul ca se intentioneaza sa se foloseasca numele pow pentru mai mult decit o singura functie.
Daca o functie nu returneaza o valoare trebuie sa se declare void: void swap(int* p, int* q)
A int t = *p;
*p = *q;
*q = t;
S

1.6 Structura programului

Un nume care se utilizeaza ca sa refere acelasi lucru in doua fisiere sursa trebuie sa fie declarat ca extern: extern double sqrt(double); extern istream cin;

Este bine ca aceste declaratii sa se plaseze intr-un fisier si apoi acesta sa se includa. De exemplu, daca declaratia pentru sqrt() este in math.h atunci putem scrie:
#include <math.h>
//........ x = sqrt(4);
Daca este intre paranteze unghiulare se include de obicei din /usr/include/CC. Altfel se folosesc ghilimele.
#include "math1.h"
#include "/usr/bs/math2.h"

Mai jos un sir se defineste intr-un fisier si se scrie in altul.
//header.h extern char* prog_name; extern void f();

Fisierul main.c este programul principal:
#include "header.h" char* prog_name = "silly, but complete"; main()
A f();
S

si fisierul f.c imprima sirul:
#include <stream.h>
#include "header.h" void f()A cout << prog_name << "\n"; S La executie se obtine textul:
$CC main.c f.c -o silly
$silly silly, but complete
$

1.7 Clase
Sa vedem cum putem defini tipul ostream. Pentru a simplifica aceasta sarcina, presupunem ca s-a definit tipul streambuf pentru buferarea caracterelor. Un streambuf este in realitate definit in <stream.h> unde se gaseste de asemenea definitia reala a lui ostream.
Definitia tipului utilizator (numit clasa in C++) contine o specificatie a datei necesare pentru a reprezenta un obiect de acest tip si o multime de operatii pentru a manevra astfel de obiecte. Definitia are doua parti: o parte privata ce pastreaza informatia care poate fi utilizata numai de implementatorul ei si o parte publica ce reprezinta o interfata cu utilizatorul: class ostreamA streambuf* buf; int state; public: void put(char*); void put(long); void put(double);
S;

Declaratiile dupa eticheta public specifica interfata; utilizatorul poate apela cele 3 functii put(). Declaratiile ce se gasesc inaintea etichetei public specifica reprezentarea unui obiect al clasei ostream. Numele buf si state pot fi utilizate numai prin functiile put() declarate in partea public.
O clasa defineste un tip si nu un obiect data, asa ca pentru a utiliza un ostream noi trebuie sa declaram unul (in acelasi mod in care noi declaram variabilele de tip int): ostream my_out;
Presupunind ca my_out a fost deja initializat in mod corespunzator, el poate fi utilizat acum astfel: my_out.put("Hello, world\n");
Operatorul se foloseste pentru a selecta un membru al clasei pentru un obiect dat al acelei clase. Aici functia membru put() se apeleaza pentru obiectul my_out.
Functia poate fi declarata astfel: void ostream::put(char* p)
A
while(*p) buf.sputc(*p++);
S

unde sputc() este o functie care pune un caracter in streambuf. Prefixul ostream este necesar pentru a distinge put() a lui ostream de alte apeluri ale lui put().
Pentru a apela o functie membru, un obiect al clasei trebuie sa fie specificat. In functia membru, acest obiect poate fi implicit referentiat asa cum se face in ostream::put() de mai sus; in fiecare apel, buf se refera la membrul buf al obiectului pentru care se apeleaza functia.
Este de asemenea posibil sa ne referim explicit la acel obiect printr-un pointer numit this. Intr-o functie membru al unei clase X, acesta este implicit declarat ca X* (pointer spre X) si initializat cu un pointer spre obiectul pentru care functia este apelata. Definitia lui ostream::put() ar putea fi scrisa astfel: void ostream::put(char* p)
A
while(*p) this->buf.sputc(*p++);
S

Operatorul -> se utilizeaza pentru a selecta un membru al unui obiect.

1.8 Operatorul overloading

Clasa reala ostream defineste operatorul << pentru a-l face convenabil sa scrie diferite obiecte cu o singura instructiune.
Pentru a defini @, unde @ este orice operator C++ pentru un tip definit de utilizator, noi definim o functie numita operator@ care are argumente de tip corespunzator. De exemplu: class ostreamA //........ ostream operator<<(char*);
S; ostream ostream::operator<<(char* p)
A
while(*p) buf.sputc(*p++); return *this;
S

defineste operatorul <<, ca membru al clasei ostream, asa ca s<<p se interpreteaza ca s.operator<<(p) cind s este un ostream si p este un pointer spre caractere. Operatorul << este binar, dar functia operator<<(char*) pare la prima vedere sa aiba un singur argument; el totusi are de asemenea argumentul standard implicit this.

Revenind din ostream ni se permite sa aplicam << la rezultatul unei operatii de iesire. De exemplu, s<<p<<q se interpreteaza (s.operator<<(p)).operator<<(q). Acesta este modul in care operatiile sint furnizate pentru tipuri predefinite.
Utilizind setul de operatii furnizate cu membri publici ai clasei ostream, noi putem acum defini << pentru tipuri utilizator cum ar fi cel complex, fara a modifica declaratia clasei ostream: ostream operator<<(ostream s, complex z) // un complex are doua parti: real si imaginar // se scrie un complex ca (real, imag)
A return s << "(" << z.real << "," << z.imag << ")";
S

Intrucit operator<<(ostream, complex) nu este o functie membru, este necesar sa explicitam argumentele ca sa fie binare. Se vor scrie valorile in ordine corecta caci <<, ca si majoritatea operatorilor C++ se grupeaza de la stinga la dreapta; adica a<<b<<c inseamna (a<<b)<<c.
Compilatorul cunoaste diferenta dintre functiile membru si nemembru cind interpreteaza operatorii. De exemplu, daca z este o variabila complexa, s<<z va fi expandata utilizind apelul functiei standard (nemembru) operator<<(s,z).

1.9 Referinte

Ultima versiune a lui ostream din nefericire contine o eroare serioasa. Problema este ca ostream este copiat de doua ori pentru fiecare utilizare a lui <<: odata ca un argument si odata ca valoare returnata. Aceasta lasa starea nemodificata dupa fiecare apel. Este nevoie de o facilitate pentru a pasa un pointer la ostream mai degraba decit sa se paseze insasi ostream.
Aceasta se poate realiza utilizind referintele. O referinta actioneaza ca un nume pentru un obiect; T& inseamna referinta la T. O referinta trebuie initializata si devine un nume alternativa pentru obiectul cu care este initializat. De exemplu: ostream& s1 = my_out; ostream& s2 = cout;
Referintele s1 si my_out pot fi utilizate acum in acelasi mod si cu acelasi inteles. De exemplu, atribuirea: s1 = s2; copiaza obiectul referit prin s2 (adica cout) in obiectul referit prin s1 (adica my_out). Membri se selecteaza utilizind operatorul punct: s1.put("don't use ->"); si daca utilizam operatorul adresa, primim adresa obiectului referit:
&s1 == &my_out
Prima utilizare evidenta a referintei este ca sa ne asiguram ca adresa unui obiect, mai degraba decit obiectul insusi, este pasata la o functie de iesire (aceasta se numeste in anumite limbaje apel prin referinta): ostream& operator<<(ostream& s, complex z)
A return s << "(" << z.real << "," << z.imag << ")";
S

Corpul functiei este neschimbat dar asignarea facuta lui s va afecta acum obiectul dat ca argument. In acest caz, returnindu-se o referinta de asemenea se imbunatateste eficienta, intru- cit modul evident de implementare a unei referinte este un pointer si un pointer este mai ieftin sa fie transferat decit o structura mare.
Referintele sint de asemenea esentiale pentru definirea sirurilor de intrare deoarece operatorului input i se da variabila in care se citeste ca operand. Daca referintele nu sint utilizate, utilizatorul ar trebui sa paseze pointeri expliciti functiilor de intrare: class istreamA //........ int state; public: istream& operator>>(char&); istream& operator>>(char*); istream& operator>>(int&); istream& operator>>(long&);
//........
S;

Sa observam ca se folosesc doua operatii separate pentru a citi intr-o zona long si intr-o zona int si numai una pentru scriere. Motivul este ca un int poate fi convertit spre long prin regulile implicite de conversie.

1.10 Constructori
Definirea lui ostream ca si clasa, face ca datele membru sa fie private. Numai o functie membru poate accesa membri privati, asa ca noi trebuie sa furnizam una pentru initializare. O astfel de functie se numeste constructor si se distinge avind acelasi nume ca si al clasei lui: class ostreamA //....... ostream(streambuf*); ostream(int size, char* s); S;
Aici se furnizeaza doi constructori. Unul ia un streambuf pentru o iesire reala iar celalalt ia o dimensiune si un pointer spre caractere pentru formatarea sirului. Intr-o declaratie, argumentul lista necesar pentru un constructor se adauga la nume. Noi putem declara acum streamuri astfel: ostream my_out(&some_stream_buffer); char xxa256i; ostream xx_stream(256,xx);
Declaratia lui my_out seteaza nu numai cantitatea corespunzatoare de memorie ci de asemenea apeleaza si constructorul ostream::ostream(streambuf*) pentru a-l initializa cu argumentul &some_stream_buffer, care este un pointer spre un obiect potrivit al clasei streambuf. Declaratia functiei xx_stream() se trateaza similar, dar utilizeaza celalalt constructor. Declarind constructori pentru o clasa nu furnizam numai un mod de a initializa obiecte, ci de asemenea se asigura ca toate obiectele clasei vor fi initializate. Cind s-a declarat un constructor pentru o clasa, nu este posibil sa se declare o variabila a acelei clase fara a apela un constructor. Daca o clasa are un constructor care nu ia argumente, acel constructor va fi apelat daca nu se da nici un argument in declaratie.

1.11 Vectori

Conceptul de vector construit in C++ a fost proiectat pentru a permite o eficienta maxima la executie si memorie minima. Este de asemenea, mai ales cind se utilizeaza impreuna cu pointerii, un instrument puternic pentru construirea unor facilitati de nivel inalt. Noi putem, totusi, sa ne plingem de faptul ca dimensiunea unui vector trebuie sa fie specificata ca o constanta, ca nu exista verificarea depasirii limitelor vectorilor, etc.. Un raspuns la aceste plingeri este: noi insine putem programa acest lucru. Sa vedem daca acesta este un raspuns rezonabil, cu alte cuvinte, sa testam facilitatile de abstractizare ale lui C++ incercind sa furnizam aceste caracteristici pentru tipurile de vectori proiectati de noi si sa observam dificultatile implicate, costurile implicate si comoditatea utilizarii tipurilor de vectori rezultate. class vectorA int* v; int sz; public: vector(int); //constructor
Ivector(); //destructor int size()Areturn sz;S void set_size(int); int& operatorai(int); int& elem(int i)Areturn vaii;S
S;

Functia size() returneaza numarul de elemente al vectorului; adica, indicii trebuie sa fie in domeniul 0..size()-1. Functia set_size() este furnizata pentru a schimba acea dimensiune, elem() furnizeaza acces la membri fara a verifica indexul, iar operatorai furnizeaza acces cu verificarea limitelor.
Ideea este de a avea clasa ca o structura de dimensiune fixa care controleaza accesul la memoria reala a vectorului, care este alocata prin constructorul vectorului utilizind operatorul new de alocare de memorie. vector::vector(int s)
Aif(s<=0) error("bad vector size"); sz = s; v = new intasi;

S
Noi putem declara vectori foarte asemanator cu vectorii care sint construiti in limbajul insusi: vector v1(100); vector v2(nelem*2-4);

Operatia de acces poate fi definita ca: int& vector::operatorai(int i)
A if(i<0 || sz<=i) error("vector index out of range"); return vaii;
S

Returnind o referinta se asigura ca notatia ai poate fi utilizata de ambele parti a unei atribuiri: v1axi = v2ayi;
Functia Ivector() este un destructor; adica este o functie declarata pentru a fi apelata implicit cind obiectul unei clase iese in afara domeniului. Destructorul pentru o clasa C se numeste IC. Daca noi o definim astfel: vector::Ivector()
A delete v;
S

ea va fi utilizata pentru a sterge operatorul si pentru a dezaloca spatiul alocat prin constructor, asa ca atunci cind un vector iese afara din domeniu, tot spatiul lui este eliberat si poate fi reutilizat.

1.12 Expandare inline

O functie membru nu este mai costisitoare la apel decit o functie nemembru cu acelasi numar de argumente (sa ne amintim ca o functie membru totdeauna are cel putin un argument), iar apelul functiilor C++ este aproximativ tot atit de eficient ca si in alte limbaje. Totusi, pentru functiile extrem de mici, apelul poate sa iasa in evidenta. Daca este asa, noi am putea dori sa specificam o functie care sa expandeze in linie. In caz afirmativ, compilatorul va genera cod propriu pentru functie in locul apelului. Semanticile apelului ramin neschimbate. De exemplu, daca size() si elem() sint substituite in linie: vector s(100);
//.......... i = s.size(); x = elem(i-1); este echivalent cu i = 100; x = s.vai-1i;
Compilatorul este destul de abil pentru a genera un cod care este tot atit de bun ca si cel care se obtine direct prin macro expandare. Evident, compilatorul are nevoie uneori sa foloseasca variabile temporare si alte citeva abilitati pentru a prezerva semanticile.
Noi dorim o indicatie a faptului ca functia se expandeaza inline care sa preceada definitia ei. Aceasta este cuvintul cheie inline sau pentru o functie membru, pur si simplu prin includerea definitiei functiei in declaratiile clasei, asa cum s-a facut pentru size() si elem() in exemplul precedent.
Cind se utilizeaza bine, functiile inline maresc simultan viteza de executie si descresc dimensiunea codului obiect. Totusi, functiile inline din declaratiile clasei pot incetini compilarea, asa ca ele trebuie sa fie eliminate cind ele nu sint necesare. Pentru ca substitutia inline sa fie un beneficiu semnificativ pentru o functie, functia trebuie sa fie foarte mica.

1.13 Clase derivate

Acum sa definim un vector pentru care un utilizator poate defini limitele indexului. class vec:public vectorA int low, high; public: vec(int,int); int& elem(int); int& operatorai(int);
S;
Definind vec ca public vector inseamna inainte de toate ca un vec este un vector. Adica tipul vec are toate proprietatile tipului vector si in plus cele specific declarate pentru el. Clasa vector se spune ca este clasa de baza pentru vec si invers vec se spune ca este derivat din vector.
Clasa vec modifica clasa vector furnizind un constructor diferit care cere utilizatorului sa specifice cele doua limite ale indexului in schimbul dimensiunii si produce accesul propriu functiilor elem(int) si operatorai(int).elem() a lui vec se exprima usor in termenii lui elem() al lui vector: int& vec::elem(int i)A return vector::elem(i-low); S
Scopul operatorului :: este de a elimina o recursivitate infinita calculind vec::elem() din el insusi. Unarul :: se poate folosi pentru a ne referi la nume nelocale. Ar fi rezonabil sa declaram vec::elem() inline din motive de eficienta, dar nu este necesar, sau este posibil sa-l scriem asa ca el sa utilizeze direct membrul privat V al clasei vector. Functiile unei clase derivate nu au nici un acces special la membri privati ai clasei de baza propri. Constructorul poate fi scris astfel: vec::vec(int lb, int hb) : (hb-lb+1)
A if(hb-lb < 0) hb = lb; low = lb; high = hb;
S

Constructia: (hb-lb+1) se utilizeaza pentru a specifica lista argument pentru constructorul clasei de baza vector::vector(). Acest constructor se apeleaza inaintea corpului lui vec::vec(). Iata un mic exemplu care poate fi executat daca se compileaza cu restul declaratiilor lui vector:

#include <stream.h> void error(char* p)
A cerr << p << "\n"; // cerr is the error output stream exit(1);
S

void vector::set_size(int)
A
/* dummy */
S

int& vec::operatorai(int i)
A if(i<low || high<i) error("vec index out of range"); return elem(i);
S

main()
A vector a(10); for(int i=0; i<a.size(); i++)
A aaii = i; cout << aaii << " ";
S cout << "\n"; vec b(10, 19); for(i=0; i<b.size(); i++) bai+10i = aaii; for(i=0; i<b.size(); i++) cout << bai+10i << " "; cout << "\n";
S

Acesta produce:

0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9

1.14 Mai mult despre operatori

O alta directie a dezvoltarii este de a furniza vectori cu operatii: class Vec::public vectorA public:
Vec(int s) : (s)A S Vec(Vec&);
IVec()A S void operator=(Vec&); void operator*=(Vec&); void operator*=(int);
//......
S;

Observam modul in care constructorul pentru clasa derivata Vec::Vec() este definit pentru a transfera argumentele lui la constructorul pentru clasa de baza vector::vector(). Operatorul de atribuire este supraincarcat si poate fi definit astfel: void Vec::operator=(Vec& a)
A int s = size(); if(s != a.size()) error("bad vector size for ="); for(int i=0; i<s; i++) elem(i) = a.elem(i);
S

Atribuirea de Vec-uri acum copiaza elemente, in timp ce atribuirea de vectori copiaza pur si simplu structura care controleaza accesul la elemente.
Totusi, ultima se intimpla cind se copiaza un vector fara utilizarea explicita a operatorului de atribuire:
(1) cind un vector este initializat prin atribuirea unui alt vector;
(2) cind un vector se paseaza ca argument;
(3) cind un vector se paseaza ca valoare returnata de la o functie.
Pentru a cistiga control in aceste cazuri pentru vectorii Vec, noi definim constructorul Vec(Vec&):
Vec::Vec(Vec& a) : (a.size())
A int sz = a.size(); for(int i=0; i<sz; i++) elem(i) = a.elem(i);
S

Acest constructor initializeaza un Vec ca o copie a altuia si va fi apelat in cazurile mentionate precedent. Pentru operatori de forma = si +=, expresia din stinga este evident speciala si se pare natural ca ei sa se implementeze ca operatii asupra obiectelor notate prin acea expresie. In particular, este posibil pentru ei sa se schimbe valoarea primului lor operand. Pentru operatori de forma + si -, operandul sting nu necesita o atentie speciala. Noi am putea, de exemplu, sa transferam ambele argumente prin valoare si totusi sa capatam o implementare corecta a adunarii vectorilor. Vectorii pot fi mari, asa ca, pentru a elimina copierea, operanzii lui + se transfera operatorului operator+() prin referinta:
Vec operator+(Vec& a, Vec& b)

A int s = a.size(); if(s != b.size()) error("bad vector size for +");
Vec sum(s); for(int i=0; i<s; i++) sum.elem(i) = a.elem(i) + b.elem(i); return sum;
S

Iata un mic exemplu care poate fi executat daca se compileaza cu declaratiile de vector prezentate anterior:
#include <stream.h> void error(char* p)
Acerr << p << "\n"; exit(1);
S void vector::set_size(int)A /*...*/ S void vec::operatorai(int i)A /*...*/ S main()
AVec a(10);
Vec b(10); for(int i=0; i<a.size(); i++) aaii = i; b = a;
Vec c = a+b; for(i=0; i<c.size(); i++) cout << caii << "\n";
S

1.15 Prieteni (Friends)

Functia operator+() nu opereaza direct asupra reprezentarii unui vector; intr-adevar, nu ar putea, deoarece nu este un membru. Totusi, uneori este de dorit ca sa se admita ca functii nemembru sa aiba acces la partea privata a unui obiect de clasa. De exemplu, neexistind functia cu acces "neverificat", vector:: elem(), noi ar trebui sa fortam verificarea indexului i fata de limitele vectorului de trei ori de fiecare data cind se executa ciclul. Aceasta problema a fost eliminata aici, dar ea este tipica, asa ca exista un mecanism pentru o clasa care sa accepte accesul la partea sa privata pentru o functie nemembru.
O declaratie a unei functii precedate prin cuvintul cheie friend este pur si simplu plasata in declaratia clasei. De exemplu, dindu-se: class Vec; // Vec este un nume de clasa class vectorA friend Vec operator+(Vec, Vec);
//...........
S;

noi putem scrie:
Vec operator+(Vec a, Vec b)
A int s = a.size(); if(s != b.size()) error("bad vector size for +");
Vec& sum = *new Vec(s); int* sp = sum.v; int* ap = a.v; int* bp = b.v; while(s--)
*sp++ = *ap++ + *bp++; return sum;
S

Un aspect particular util al mecanismului de prieten (friend) este ca o functie poate fi prieten a doua sau mai multe clase. Pentru a vedea aceasta, sa consideram definirea unui vector si a unei matrici si apoi definirea functiei de inmultire.

1.16 Vectori generici

Noi am dori, de exemplu, unul din acei vectori pentru tipul matrice pe care l-am definit. Din nefericire, C++ nu furnizeaza o facilitate pentru a defini o clasa vector cu tipul elementelor ca argument. Un mod de a proceda ar fi sa se copieze atit definitia clasei cit si functiile membru. Acest lucru nu este ideal, dar adesea este acceptabil. Noi putem utiliza macroprocesor pentru a mecaniza acel task. De exemplu, clasa vector este o versiune simplificata a unei clase care poate fi gasita intr-un fisier header standard. Noi am putea scrie:
#include <vector.h> declare(vector, int); main()
A vector (int)vv(10); vva2i = 3; vva10i = 4; //eroare de rang
S

Fisierul vector.h defineste macrouri asa ca declare(vector, int) se expandeaza spre declaratia unei clase vector foarte asemanatoare cu cea definita, iar implement(vector, int) se expandeaza spre definitiile functiilor acelei clase.Intrucit implement(vec- tor, int) se expandeaza in definitii de functii, el poate fi utilizat numai odata intr-un program, in timp ce declare(vector, int) trebuie sa fie utilizat odata in fiecare fisier care manipuleaza acest fel de vectori intregi. declare(vector, int);
//...... implement(vector, char); da un tip (separat) "vector de caractere".

1.17 Vectori polimorfici
O alta varianta ar fi ca sa definim vectorul nostru si cu alte clase container in termenii unor pointeri la obiectele unei anumite clase: class commonA /*........*/S; class vectorA common** v;
//...... public: cvector(int); common*& elem(int); common*& operatorai(int);
//......
S;

Sa observam ca deoarece pointerii si nu obiectele insasi sint memorati intr-un astfel de vector, un obiect poate fi "in" diferiti astfel de vectori in acelasi timp. Aceasta este o caracteristica foarte utila pentru clasele container de felul vectorilor, listelor inlantuite, multimilor, etc.. Mai mult decit atit, un pointer la o clasa derivata poate fi atribuit la un pointer spre clasa ei de baza, asa ca cvector de mai sus poate fi utilizat pentru a pastra pointeri spre obiectele tuturor claselor derivate din common. De exemplu: class apple : public commonA /*...*/ S; class orange : public commonA /*...*/ S; class apple_vector : public cvectorA public: cvector fruitbowl(100);
//...... apple aa; orange oo; fruitbowla0i = &aa; fruitbowla1i = &oo;
S;

Totusi, tipul exact al unui obiect intr-o astfel de clasa container nu mai este cunoscut de compilator. De exemplu, in exemplul precedent noi stim ca un element al vectorului este un common, dar este un apple sau un orange ? In mod obisnuit, tipul exact trebuie sa fie descoperit mai tirziu pentru a putea utiliza corect obiectul. Pentru a face aceasta, noi trebuie sau sa memoram o anumita forma a tipului de informatie in obiectul insusi sau sa ne asiguram ca numai obiectele unui tip dat se pun in container. Ultima varianta este atinsa trivial utilizind o clasa derivata. De exemplu, noi am putea face un vector de pointeri apple: class apple_vector : public cvectorA public: apple*& elem(int i)
A return (apple*&)cvector::elem(i); S
//......... S; utilizind notatia de type_casting. common*& (o referinta la pointer spre common) returnat prin cvector::elem spre apple*&. Aceasta utilizare a claselor derivate furnizeaza o alternativa a claselor generice. Este putin mai greu sa scriem in acest fel (daca nu sint utilizate macrouri asa incit clasele derivate sa fie de fapt utilizate pentru a implementa clase generice), dar are avantajul ca toate clasele derivate au in comun o singura copie a functiilor clasei de baza.
Pentru o clasa generica de felul lui vector(type), trebuie sa se faca o noua copie a acelor functii (prin implement()) pentru fiecare tip nou utilizat.
Alternativa de a memora identificatorul tipului in fiecare obiect ne conduce spre un stil de programare adesea referit ca bazat sau orientat spre obiect.

1.18 Functii virtuale
Sa consideram scrierea unui program pentru afisarea formelor pe un ecran. Atributele comune ale formelor se reprezinta prin clasa shape, atribute specificate prin clase derivate specifice: class shapeApoint center; color col;
//....... public: void move(point to)
A center = to; draw();
S point where()A return center; S virtual void draw(); virtual void rotate(int);
//........
S;
Functiile care pot fi definite fara cunostinte despre forma specifica (de exemplu move si where), pot fi declarate in mod obisnuit. Restul se declara virtual, adica se vor defini intr-o clasa derivata. De exemplu: class circle : public shapeA int radius; public: void draw(); void rotate(int i)AS
//.......
S;
Acum daca shape_vec este un vector de forme, noi putem scrie: for(int i=0; i<no_of_shapes; i++) shape_vecaii.rotate(45); pentru a roti (si redesena) toate formele cu 45 de grade.
Acest stil este deosebit de util in programe interactive cind obiectele de tipuri diferite sint tratate uniform de catre softwareul de baza.


Colt dreapta
Creeaza cont
Comentarii:

Nu ai gasit ce cautai? Crezi ca ceva ne lipseste? Lasa-ti comentariul si incercam sa te ajutam.
Esti satisfacut de calitarea acestui document, eseu, cometariu? Apreciem aprecierile voastre.

Nume (obligatoriu):

Email (obligatoriu, nu va fi publicat):

Site URL (optional):


Comentariile tale: (NO HTML)


Noteaza documentul:
In prezent fisierul este notat cu: ? (media unui numar de ? de note primite).

2345678910

 
Copyright© 2005 - 2024 | Trimite document | Harta site | Adauga in favorite
Colt dreapta