Sunteți pe pagina 1din 14

Valeriu Iorga Programare în C / C++

Clase.

1. Programare procedurală – Programare orientată pe obiecte.


2. Declararea claselor.
3. Operatorul de rezoluţie.
4. Funcţii membre inline.
5. Referinţe.
6. Constructori şi destructori.
7. Pointerul this.
8. Membri statici ai claselor.
9. Funcţii membre constante.
10. Funcţii care returnează referinţe.
11. Probleme.

1. Programare procedurală –Programare orientată pe obiecte.


Limbajul C, ca şi Pascal, utilizează modelul programării structurate procedurale, care constă în
descompunerea programului în proceduri (funcţii), apelate în ordinea în care se desfăşoară algoritmul.
Datele sunt separate de funcţiile care le manipulează.
Odată cu creşterea dimensiunii programelor s-a acordat o atenţie sporită organizării datelor –
funcţiile, împreună cu datele pe care le manevrează sunt organizate ca un modul. Programarea modulară
este tot programare procedurală, cu proceduri şi date grupate în module şi ascunse altor module.
Programarea orientată pe obiecte constă în identificarea unor obiecte, cu operaţii (metode)
specifice asociate şi realizarea comunicării între aceste obiecte prin intermediul unor mesaje. Elementul
constructiv – obiectul este o instanţă a unei clase (tip de dată definită de utilizator). Clasele sunt membre
ale unei ierarhii şi sunt corelate între ele prin relaţii de moştenire.
Un limbaj de programare pune la dispoziţia utilizatorilor un număr de tipuri primitive (sau
predefinite). Astfel în C avem ca tipuri primitive char, int, float, etc. Un tip de date este precizat
printr-o mulţime finită de valori T (constantele tipului) şi o mulţime de operatori (aplicaţii T  T sau
T  T  T).
Într-un limbaj de programare un tip de date reprezintă un model matemaţic. Astfel tipul int ,
caracterizat prin mulţimea finită de întregi (-32768, 32767) şi operatorii binari +, -, *, / şi %
ilustrează conceptul de număr întreg.
Modelele matemaţice care nu au reprezentare directă prin tipuri predefinite se pot reprezenta prin
tipuri definite de utilizator (numite şi tipuri de date abstracte- TDA).

2. Declararea claselor.
O clasă reprezintă un tip definit de utilizator. Declararea unei clase se face într-o manieră
asemănătoare declarării structurilor şi conţine atât date cât şi funcţii (metode) şi putem declara variabile de
acest tip nou. Un obiect este un exemplar sau o instanţă a unei clase (în vechea terminologie obiectul este
echivalent unei variabile, iar clasa este echivalentul unui tip definit de utilizator).
Considerăm conceptul Data, pe care-l reprezentăm printr-o structură şi un set separat de funcţii
de manipulare:
struct Data{
int a, l, z; // reprezentare
};
void initD(const Data&, int, int, int); //initializare
void ad_a(const Data&, int); //adauga un an
void ad_l(const Data&, int); //adauga o luna
void ad_z(const Data&, int); //adauga o zi

1
Valeriu Iorga Programare în C / C++

Nu există o legătură implicită între date şi funcţiile de manipulare a lor. Pentru a stabili această
legătură, declarăm funcţiile ca membre ale structurii:
struct Data{
int a, l, z; // reprezentare
..// functii de manipulare
void initD(int, int, int); // initializare
void ad_a(int);
void ad_l(int);
void ad_z(int);
};
Funcţiile declarate în definirea clasei (structura este o clasă) se numesc funcţii membre şi pot fi
apelate numai de variabile de tipul corespunzător (obiecte) folosind sintaxa de acces la membrii structurii:
Data d;
d.initD(2001, 10, 15);
d.ad_a(3);
. . .
Definirea funcţiilor membre, în afara clasei se face folosind operatorul de vizibilitate (rezoluţie),
care indică faptul că funcţia aparţine clasei specificate înaintea acestui operator.
void Data::initD(int aa, int ll, int zz){
a = aa;
l = ll;
z = zz;
};
Declararea unei clase se face în mod asemănător cu declararea unei structuri. Cuvântul struct
este înlocuit prin class, iar câmpurile se separă în date membre şi funcţii membre (sau metode). În plus,
se pot preciza specificatori de acces la membri. Aceştia pot fi:
 private – membrii sunt accesibili numai din interiorul clasei
 public – membrii sunt accesibili din afara clasei
 protected – membrii sunt accesibili din interiorul clasei şi din clasele derivate
Domeniul de definiţie al clasei este cuprins între începutul definiţiei clasei (class nume) şi
sfârşitul definiţiei.
Domeniul unui specificator de acces se întinde din locul unde apare, până la următorul specificator
de acces. Dacă nu apare nici un specificator de acces, accesul la membrii clasei nu va fi permis din afara ei
(implicit se consideră specificatorul private).
Datele clasei (definind structura sau starea) fiind private sunt “încapsulate”, adică ascunse
utilizatorului. Acesta nu le poate accesa direct, ci numai prin intermediul unor funcţii de acces publice, care
aparţin interfeţei ce defineşte comportarea clasei. (de obicei datele sunt private şi funcţiile sunt publice.)
//declararea clasei (interfata)
class Data{
int a, l, z; // reprezentare
public:
void initD(int, int, int);
void ad_a(int);
void ad_l(int);
void ad_z(int);
};
Variabilele ce apar în declaraţia clasei (datele membre), fiind private, pot fi folosite numai de către
funcţiile membre.
Funcţiile membre din partea publică pot fi accesate de către oricine. O structură este o clasă cu toţi
membrii publici.

2
Valeriu Iorga Programare în C / C++

Un alt exemplu - clasa punct; are ca date membre – abscisa şi ordonata, iar accesul la ele va fi
oprit; ca funcţii membre vom prevedea: iniţializarea, funcţii de acces la date, de setare date şi afişarea.
#include <iostream.h>
#include <iomanip.h>

// declaraţia clasei
class punct{
private:
double x0, y0; // date membre

public:
void init(double x=0, double y=0){
x0 = x;
y0 = y;
};
void setx(double x){ x0 = x; };
void sety(double y){ y0 = y; };
double getx(){ return x0; };
double gety(){ return y0; };
void afisare(){
long f = ios::fixed;
cout << setiosflags(f) << setw(6) << setprecision(2);
cout << “(“ << x0 << “,” << y0 << “)” << endl;
};
};
void main(){
punct z1, z2, z;
z1.init(1, 2);
z2.setx(3);
z2.sety(4);
z.init(z1.getx(),z2.gety());
z.afisare();
}
Se preferă ca definirea funcţiilor clasei (în afara cazurilor în care acestea sunt foarte mici), să se
facă în afara clasei, funcţiile membre fiind date numai prin semnăturile lor. În acest caz putem separa
definirea clasei de implementarea ei. Utilizatorului clasei i se asigură ca interfaţă, numai definirea clasei,
fiindu-i ascunsă implementarea ei.
// definirea clasei – fisierul punct.h
class punct{
private:
double x0, y0; // date membre
public:
void init(double x=0, double y=0);
void setx(double x=0);
void sety(double y=0);
double getx();
double gety();
void afisare();
};

3. Operatorul de rezoluţie.
Întrucât definirea funcţiilor se va face în afara domeniului de definiţie al clasei, numele funcţiei
trebuie să fie însoţit de numele clasei, şi să fie separat de aceasta prin operatorul de rezoluţie sau
vizibilitate ( :: ). Aşadar, implementarea clasei, (fişierul punct.cpp) va fi:

3
Valeriu Iorga Programare în C / C++

// fisierul punct.cpp
#include <iostream.h>
#include <iomanip.h>
void punct::init(double x, double y){
x0 = x;
y0 = y;
};
void punct::setx(double x){ x0 = x; };
void punct::sety(double y){ y0 = y; };
double punct::getx(){ return x0; };
double punct::gety(){ return y0; };
void punct::afisare(){
long f = ios::fixed;
cout << setiosflags(f) << setw(6) << setprecision(2);
cout << “(“ << x0 << “,” << y0 << “)” << endl;
};
};
De obicei declararea clasei (interfaţa) şi definirea ei (implementarea) se fac în fişiere separate.
Declaraţia unei clase se face într-un fişier antet. Un fişier antet trebuie inclus o singură dată în fişierul
programului.
Pentru a evita includerea multiplă de fişiere antet se utilizează directive #ifndef sub forma:
#ifndef _simbol_
#define _simbol_
declaraţie clasă (fişier antet)
#endif
Programul care utilizează clasa punct este un program constituit din mai multe fişiere, care vor
alcătui un proiect. El va conţine o directivă de includere a fişierului de interfaţă punct.h pentru definirea
clasei. La prima întâlnire a fişierului antet, simbolul nu este definit, incluzându-se fişierul antet. După
prima includere, simbolul fiind definit se ignoră porţiunea cuprinsă între #define şi #endif.
Fişierul de implementare conţine definiţiile metodelor, directiva de includere a fişierului antet al
interfeţei şi alte directive de includere de fişiere.
#include <iostream.h>
#include “clasa.h”
// definitii funcţii membre ale clasei
Programul utilizator (al treilea fişier) va conţine: includerea diverselor fişiere antet, includerea
fişierului antet al clasei şi funcţia main().
#include <iostream.h>
#include “clasa.h”
void main(){
. . .
}
4. Funcţii membre inline.
Funcţiile mici, cu puţine instrucţiuni, apelate frecvent se compilează ineficient (se rezervă spaţiu
în stivă pentru parametrii şi rezultat, se efectuează salt la funcţie şi apoi salt pentru revenire). Mult mai
eficientă ar fi expandarea apelului funcţiei prin corpul ei, ca în macroinstrucţiuni. Apelul unei funcţii
declarate inline este înlocuit la compilare prin corpul funcţiei (apel realizat prin expandare). O funcţie
inline nu trebuie să conţină bucle.

4
Valeriu Iorga Programare în C / C++

Funcţiile membre definite în interiorul clasei sunt în mod implicit inline, dar este posibil ca şi
funcţii definite în afara clasei să fie declarate explicit inline (expandate).
class Data{
public:
int zi();
. . .
private:
int a, l, z;
};
// functie declarata in mod explicit inline inafara clasei
inline int Data::zi(){return z;};

// definirea clasei – fisierul punct.h


class punct{
private:
double x0, y0; // date membre
public:
inline void init(double x=0, double y=0); //nu era necesara
inline void setx(double x=0){ x0=x; }; //declararea explicita
inline void sety(double y=0){ y0=y; }; //erau considerate inline
double getx(){ return x0; }; // este implicit inline
double gety(){ return y0; };
void afisare();
};
// fisierul punct.cpp
#include <iostream.h>
#include <iomanip.h>
//desi este definita in afara clasei este inline (apel expandat)
inline void punct::init(double x, double y){
x0 = x;
y0 = y;
};
//nu este inline (apel=salt cu revenire)
void punct::afisare(){
long f = ios::fixed;
cout << setiosflags(f) << setw(6) << setprecision(2);
cout << “(“ << x0 << “,” << y0 << “)” << endl;
};
};
5. Referinţe.
O referinţă se comportă ca un pointer constant, care este în mod automat dereferenţiat. Referinţele
se folosesc:
- în listele de parametri ale funcţiilor
- ca valori de întoarcere ale funcţiilor
La creerea unei referinţe, aceasta trebuie iniţializată cu adresa unui obiect (nu cu o valoaren
constantă).
int x;
int& rx = x; //referinta initializata cu o adresa
Referinţa poate fi privită ca un pointer inteligent, a cărui iniţializare este forţată de către
compilator (la definire) şi care este dereferenţiat automat.

5
Valeriu Iorga Programare în C / C++

Spre deosebire de pointeri:


- referinţele sunt iniţializate la creere (pointerii se pot iniţializa oricând)
- referinţa este legată de un singur obiect şi această legătură nu poate fi modificată pentru un alt
obiect
- nu există referinţe nule – ele sunt totdeauna legate de locaţii de memorie

6. Constructori şi destructori.
Iniţializarea asigurată de funcţia membru init() este lăsată la laţitudinea utilizatorului. Este de
preferat o iniţializare mai sigură a obiectului, care să se facă în mod implicit la declararea obiectului.
Iniţializarea obiectelor se face prin intermediul unor funcţii speciale numite constructori.
Folosirea funcţiei initD() pentru iniţializarea obiectelor clasei Data este nesigură – putem
uita să facem iniţializarea sau să facem o iniţializare repetată.
Un obiect poate fi declarat neiniţializat sau iniţializat. Exemple:
Complex z; // obiect neiniţializat
Complex z1(-1.5, 2.); // obiect initializat
O soluţie sigură o reprezină declararea unei funcţii având scopul explicit de iniţializare a
obiectelor. O asemenea funcţie poartă numele de constructor.
Constructorul este o funcţie publică, care iniţializează datele membre, având acelaşi nume cu
numele clasei şi care nu întoarce nici o valoare.
class Data{
. . .
public:
Data(int, int, int); // constructorul
. . .
};
Considerăm definiţia clasei Complex:
#include <stdio.h>
class Complex{
private:
double re, im;
public:
void scrie();
};
void Complex::scrie(){
printf(“(%4.1lf , %4.1lf)\n”, re, im);
};
void main(){
Complex z; //obiect neinitializat
z.scrie();
}
Programul este corect din punct de vedere sintactic. Lipsa unui constructor definit de utilizator,
care să iniţializeze obiectele este remediată de compilator. Astfel, pentru clasa C, dacă nu se defineşte în
mod explicit un constructor, compilatorul generează în mod implicit constructorul: C::C(){}, care
crează datele membre ale clasei. Acest constructor implicit nu face nici o iniţializare a datelor membre, aşa
că se vor afişa nişte valori foarte mari, reprezentând conţinutul memoriei neiniţializate.
Pentru a evita aceasta, se recomandă programatorului să îşi definească un constructor implicit
(fără parametri), care să iniţializeze datele membre (în exemplul nostru iniţializarea la 0 pentru partea reală
şi cea imaginară a obiectului număr complex). Constructorul implicit acţionează asupra obiectelor
neiniţializate.

6
Valeriu Iorga Programare în C / C++

Clasa Complex, prevăzută cu acest constructor este:


class Complex{
private:
double re, im;
public:
Complex(){re=0.; im=0.};
void scrie();
};
Se preferă scrierea constructorului, folosind o listă de iniţializare, conform sintaxei:
Complex() : re(0.), im(0.){};
De această dată, în urma execuţiei se va afişa ( 0.0 , 0.0).
Dacă se declară obiecte iniţializate, iniţializarea acestora trebuie făcută de către un constructor de
iniţializare, care trebuie definit în mod explicit de către utilizator.
Pentru clasa Complex, un constructor de iniţializare, are forma:
class Complex{
private:
double re, im;
public:
Complex(double x, double y) : re(x), im(y){};
void scrie();
};
void main(){
Complex z1(-1., 2.); //obiect initializat
Complex z2; //obiect neinitializat
. . .
}
Dacă se declară un constructor de iniţializare, compilatorul nu mai generează un constructor
implicit, astfel că pentru obiectele neiniţializate (în exemplul de mai sus z2)şi tablourile de obiecte se va
genera eroare. Putem defini deci doi constructori (şi în general oricâţi, având în mod evident semnături
diferite), unul implicit şi celălalt de iniţializare:
Complex() : re(0.), im(0.){}; //ctor implicit
Complex(double x, double y):re(x),im(y){}; //ctor de initializare
Clasa de mai jos defineşte mai mulţi constructori:
class Data{
int a, l, z;
public:
Data(int, int, int); // a, l, z
Data(int, int); // l, z
Data(int); // z
Data(); // implicit cu data curenta
Data(const char*); // data reprezentata de sir
. . .
};
Se preferă înglobarea constructorului implicit în constructorul de iniţializare, folosind parametri
cu valori implicite. Constructorul de iniţializare poate fi folosit drept constructor implicit, dacă toţi
parametrii sunt prevăzuţi cu valori implicite.
Complex(double x=0., double y=0.):re(x),im(y){};
Folosirea argumentelor implicite poate reduce numărul de constructori.

7
Valeriu Iorga Programare în C / C++

În exemplul următor, în lipsa argumentelor, constructorul asigură iniţializarea datei cu valori


preluate din obiectul static azi.
class Data{
int a, l, z;
public:
Data(int aa=0, int ll=0, int zz=0);
. . .
};
Data::Data(int aa, int ll, int zz){
a = aa? aa : azi.a;
l = ll? Ll : azi.l;
z = zz? zz : azi.z;
};
Utilizatorul este obligat să-şi definească proprii constructori numai în situaţiile în care obiectul
alocă dinamic spaţiu în memorie.
Nu pot fi definiţi mai mulţi constructori impliciţi .(fiind supraîncărcaţi, diferiţii constructori
trebuie să aibă semnături diferite).
Un constructor cu un singur argument defineşte o funcţie pentru conversia de tip de la tipul
argumentului la tipul clasei. Deci vom putea iniţializa un obiect printr-o atribuire de forma:
clasa ob=argument;
Atriburea de mai sus poate fi considerată ca o conversie implicită. Pentru a o dezactiva,
constructorul cu un argument trebuie să fie precedat de cuvântul cheie explicit.
Operaţia de eliminare a unui obiect din memorie este realizată de o funcţie destructor, având
acelaşi nume cu al clasei, fără argumente, care nu întoarce nici o valoare. Pentru a face distincţie faţă de
constructor, numele destructorului începe cu ~.
Un destructor pentru un obiect este apelat în mod implicit la ieşirea din domeniul de definiţie al
obiectului sau la sfârşitul programului, pentru obiectele globale sau statice. Sunt foarte rare situaţiile în care
programatorul apelează explicit un destructor. Dacă o clasă nu are definit destructor, compilatorul
generează un destructor implicit.
Programatorul nu apelează în mod explicit constructorii sau destructorul. Constructorii se apelează
în mod implicit la declararea obiectelor. Destructorul este apelat implicit la ieşirea din blocul în care este
declarat obiectul.
Obiectele create (cu new[])de către un constructor explicit trebuiesc distruse (cu delete [])
de către un destructor definit în mod explicit.
La definirea unei funcţii constructor sau destructor nu trebuie definit tipul rezultatului întors de
funcţie (nici măcar void).
Iniţializarea obiectului de către constructor se poate face prin iniţializarea datelor membre cu
valorile parametrilor sau prin copierea datelor membre ale unui obiect transmis ca parametru. În acest din
urmă caz, avem de a face cu un constructor de copiere.
Un obiect poate fi iniţializat cu o copie a unui alt obiect al clasei sub forma clasa ob1(ob2);
De exemplu:
Complex z1(1., -2.), z2(z1);
O operaţie de tipul Clasa ob = Clasa(initializare) prin intermediul constructorului,
crează un obiect şi îi iniţializează datele membre. Exemplu:
Complex z2 = Complex(1., -2.);
are acelaşi efect cu declaraţia precedentă. Spre deosebire de acesta:

8
Valeriu Iorga Programare în C / C++

Complex z2;
Z2 = Complex(1., -2.);
crează un obiect temporar, pe care îl copiază într-un obiect existent.
Copierea unui obiect folosind operatorul de atribuire realizează o copiere membru cu membru a
obiectului sursă (din dreapta) în obiectul destinaţie (din stânga)
Data d=d1; //atribuire
Data *pd = new Data(d1); //creare obiect anonim initializat
Copia se face membru cu membru. Obiectul ce urmează a fi copiat este transmis prin referinţă.
Compilatorul va genera pentru clasa C, un constructor implicit de copiere: C::C(const C&); care
realizează copierea datelor membru cu membru (copiere superficială). Exemplu:
punct p1;// apeleaza constructorul implicit scris de programator
// care asigura initializarea la (0, 0)
punct p2(2,5); //initializare explicita (ctor de initializare)
punct p3(p2); //initializare prin copiere (ctor de copiere)
Constructorul implicit de copiere nu funcţionează corect în caz că obiectul alocă dinamic
memorie. În acest caz programatorul trebuie să-şi definească propriul constructor de copiere, care să
realizeze o copiere profundă, a datelor indicate de pointeri şi nu o copiere a pointerilor.
Obiectele clasei pot fi copiate prin atribuire. Copierea se face membru cu membru. Exemplu:
Complex z1(-1., 2.);
Complex z = z1;
Data d1=Data(2004,10,5);
Iniţializarea este mai eficientă decât atribuirea. În cazul atribuirii există o valoare (veche) a
obiectului, care trebuie ştearsă şi prin atribuire se copiază valoarea nouă a obiectului din dreapta
operatorului de atribuire.
Vom considera un exemplu în care se alocă dinamic memorie pentru datele membre. Fie clasa
persoana, prevăzută cu mai mulţi constructori şi un destructor:
class persoana{
private:
char* nume;
char* adresa;
char* cnp;
public:
persoana(char* n=””,char* a=””,char* c=””);
persoana(persoana& p);
~persoana();
};
persoana::persoana(char* n, char* a, char* c){
nume=new char[strlen(n)+1];
strcpy(nume, n);
adresa=new char[strlen(a)+1];
strcpy(adresa, a);
cnp=new char[strlen(c)+1];
strcpy(cnp, c);
};
persoana::persoana(persoana& p){
nume=new char[strlen(p.nume)+1];
strcpy(nume, p.nume);
adresa=new char[strlen(p.adresa)+1];
strcpy(adresa, p.adresa);
cnp=new char[strlen(p.cnp)+1];
strcpy(cnp, p.cnp);

9
Valeriu Iorga Programare în C / C++

};
persoana::~persoana(){
delete [] nume;
delete [] adresa;
delete [] cnp;
};
Dacă o clasă conţine date membre constante sau referinţe, acestea nu pot fi iniţializate în corpul
constructorului, ci printr-o listă de iniţializare plasată după antetul constructorului şi separată de acesta prin
Constructorii şi destructorii sunt apelaţi implicit la definirea, respectiv distrugerea obiectelor.
Obiectele sunt construite în ordinea declarării lor şi sunt distruse în ordine inversă declarării.
Clasele care conţin membri const şi membri referinţe trebuie să aibe un constructor definit de
programator, întrucât referinţele trebuiesc iniţializate.
În absenţa unor declarări explicite, compilatorul furnizează următoarele funcţii membre:
- un constructor implicit
- un constructor de copiere
- un operator de atribuire
- un destructor implicit
- un operator de adresare
7. Pointerul this.
O funcţie care operează asupra unei structuri, o va accesa prin intermediul unui parametru pointer
la această structură. O funcţie membru al unei clase, apelată dintr-un obiect, pentru a accesa membrii date
ai obiectului, va avea un parametru implicit pointer la obiect. Acest pointer poartă numele de pointerul
this, şi se declară într-o clasă C ca:
C* const this;
Fiind cuvânt cheie, el nu poate fi declarat explicit; fiind un pointer constant el nu poate fi
modificat. Referirea la datele membre x0 şi y0 se face prin intermediul acestui pointer ascuns ca this-
>x0 şi this->y0. O funcţie membru care returnează un obiect o poate face prin return *this
(returnarea unui pointer la obiect se face prin return this).
8. Membri statici ai claselor.
În fiecare obiect, pentru datele membre nestatice se crează copii. Pentru funcţiile membre există o
copie unică.
Pentru datele membre statice există o singură copie care va fi partajată de către toate obiectele.
Funcţiile membre statice sunt accesibile numai în fişierul în care sunt definite, fiind independente
de obiecte (instanţele claselor).
O funcţie statică poate fi apelată:
 ca funcţie membră a unui obiect
 independent de obiect, folosind operatorul de rezoluţie.
Pointerul this nu poate fi folosit de către funcţia statică, deoarece nu îi este transmis, aşadar
funcţia statică nu poate accesa decât date membre statice şi date globale.
Clasa Data depinde de variabila statică azi. Aceasta va aparţine clasei, dar nu şi obiectelor
clasei.
class Data{
int a, l, z;
static Data azi; //data membra statica
public:
Data(int aa=0, int ll=0, int zz=0);

10
Valeriu Iorga Programare în C / C++

. . .
static void set_data_crta(int aa, int ll, int zz);
};

Data::Data(int aa, int ll, int zz){


a = aa? aa : azi.a;
l = ll? ll : azi.l;
z = zz? Zz : azi.z;
};
Funcţia set_data_crta() permite modificarea datei curente, care iniţializează un obiect
neiniţializat. La definirea unui membru static nu se mai repetă cuvântul static.
void Data::set_implic(int a, int l, int z){
azi = Data(a, l, z);
};

9. Funcţii membre constante.


Funcţiile membre care nu modifică starea obiectului se declară ca funcţii constante (funcţii
const), punând după lista de parametri cuvântul cheie const. Compilatorul va sesiza orice încercare de
modificare a obiectului din funcţia const.
Funcţiile membre apelate din obiectele constante pot fi numai funcţii constante.
Dacă funcţia membră const este definită în afara clasei, este necesar sufixul const în definiţie.
Exemplu:
class Data{
int a, l, z;
public:
int zi()const {return z;};
int luna()const {return l;};
int an()const;
};

inline int Data::an()const{ return a;};


Nu este posibilă declararea unei constante simbolice în interiorul unei clase. Astfel:
class Vector{
const int N=10;
double x[N];
. . .
}
nu funcţionează corect deoarece declararea clasei descrie obiectul, dar nu îl crează. Sunt posibile două
soluţii:
- se declară o enumerare în interiorul clasei, care furnizează la nivelul clasei nume simbolice pentru
constante întregi :
class Vector{
enum{ N=10 };
double x[N];
. . .
}
- se crează o constantă statică, partajată de toate obiectele Vector
class Vector{
static const int N=10;
double x[N];
. . .

11
Valeriu Iorga Programare în C / C++

}
10. Funcţii care returnează referinţe.
Pentru clasa complex, am definit funcţiile care asigură accesul partea reală, respectiv imaginară
a unui număr complex,:
double real(){ return re; };
double imag(){ return im; };
Dacă am dori modificarea părţii reale a unui număr complex printr-o atribuire de forma:
z.real()=5.;
constatăm că funcţia astfel definită nu poate apărea în partea stângă a unei atribuiri (nu este o L-valoare).
Acest neajuns se remediază impunând funcţiei să returneze o referinţă la obiect, adică:
double& real(){ return re; };
Funcţiile de actualizare a datei ad_a(), ad_l(), ad_z() nu întorc valori. Pentru asemenea
funcţii este util să întoarcem o referinţă la obiectul actualizat, astfel încât operaţiile să poată fi înlănţuite de
forma:
d.ad_a(1).ad_l(1).ad_z(1);
Funcţiile se declară cu valoare de întoarcere referinţă la Data:
class Data{
Data& ad_a(int);
Data& ad_l(int);
Data& ad_z(int);
};

Data& ad_a(int n){


if(z==29 && l==2 && !bisect(a+n)){
z=1;
l=3;
};
a += n;
return *this;
}
Într-o funcţie membru nestatică this este un pointer la obiectul din care s-a apelat funcţia
membru. Nu este posibil să preluăm adresa lui this şi nici să o modificăm.
Cele mai multe referiri la this sunt implicite. Orice referire la un membru nestatic din interiorul
clasei foloseşte în mod implicit pe this pentru a obţine membrul din acel obiect.
O clasă conţine în mod uzual: un constructor, funcţii pentru examinarea obiectelor (funcţii de
acces), funcţii pentru manipularea obiectelor, operaţii de copiere, etc.

11. Probleme.

1. Definiţi şi implementaţi clasa Cerc, având ca dată membrău Raza, un constructor şi ca funcţii membre
Aria şi Circumferinta.

2. Definiţi şi implementaţi clasa Cilindru, având ca date membre Raza şi Inaltimea cilindrului şi
ca funcţii membre: un constructor, Aria şi Volumul.

3. Definiţi şi implementaţi clasa Televizor, având ca date membre: Volumul şi Canalul şi ca funcţii
membre: un constructor, Pornire, Oprire, Schimbare_Volum, Schimbare_Canal.

12
Valeriu Iorga Programare în C / C++

4. Definiţi şi implementaţi clasa Dreptunghi, având ca date membre: Lungimea şi Laţimea şi ca


funcţii membre: un constructor, SetLungime, SetLaţime, GetLungime, GetLaţime, Arie
şi Perimetru.

5. Specificaţi, proiectaţi şi implementaţi o clasă care poate fi folosita într-un program pentru a simula o
combinaţie de deschidere a unui seif.
Seiful are un buton circular cu numere de la 0 la 39 şi pentru deschiderea lui este necesară
formarea unei combinaţii de 3 numere, fie acestea x, y şi z.
Pentru deschiderea seifului, se roteste în sens orar butonul până la formarea numărului x, apoi se
roteste în sens antiorar până la formarea lui y, şi în final se formează z prin rotire în sens orar.
Clasa Seif va avea un constructor, care iniţializează combinaţia codului de deschidere cu 3
numere date ca argumente (ca argumente implicite se iau 0,0,0).
Sunt prevăzute următoarele funcţii membre:
- modificarea codului de deschidere la o noua combinaţie de 3 numere
- rotirea butonului într-o directie dată, până când se formează un număr dat
- închiderea seifului
- încercarea de a deschide seiful
- testarea stării seifului (deschis sau închis)
- indicarea combinaţiei de cod încercate pentru deschidere.

6. Specificaţi, proiectaţi şi implementaţi o clasă Punct3, folosite pentru reprezentarea punctelor (prin 3
coordonate x,y,z) în spaţiul 3-dimensional.
Asiguraţi următoarele funcţii membre:
- constructor pentru setarea unui punct într-o poziţie specificată (implicit în 0,0,0)
- mutarea unui punct cu valori specificate pentru cele 3 direcţii
- aflarea coordonatelor unui punct
- rotaţia unui punct cu un unghi specificat de-a lungul uneia dintre axele x,y sau z.
Coordonatele noului punct x',y',z' sunt:
rotaţie în jurul axei x:
x'=x
y'=y*cos(t)-z*sin(t)
z'=y*sin(t)+z*cos(t)

rotaţie în jurul axei y:


x'=x*cos(t)+z*sin(t)
y'=y;
z'=-x*sin(t)+z*cos(t)

rotaţie în jurul axei z:


x'=x*cos(t)-y*sin(t)
y'=x*sin(t)+y*cos(t)
z'=z
Se vor defini ca funcţii nemembre:
- distanţa între doua puncte
- testul dacă 2 puncte se confundă, prin redefinirea operatorului ==
- afişarea coordonatelor unui punct la ieşirea standard, prin supraîncărcarea operatorului <<
Se va defini ca funcţie prieten citirea coordonatelor unui punct de la intrarea standard, prin
supraîncărcarea operatorului >>

7. Proiectaţi şi implementaţi o clasă Aleator, care generează o secvenţă de numere întregi


pseudoaleatoare, folosind metoda congruenţei liniare.
In această metodă se folosesc 4 întregi: sămânţa, înmulţitorul, incrementul şi modulul. cu formula:

13
Valeriu Iorga Programare în C / C++

(samanta * înmultitor + increment) % modul


se generează câte un număr aleator, care devine sămânţă. În acest mod se generează "modul" numere
diferite.
Constructorul clasei are ca argumente: sămânţa iniţială, înmulţitorul,incrementul şi modulul.
Se vor prevedea funcţii membre pentru:
- schimbarea sămânţei
- generarea următorului număr din secvenţa de numere aleatoare

8. Definiţi şi implementaţi clasa Calendar având ca date membre: An, Luna, Zi, NumeZi
(enumerarea de Luni până Duminica) şi ca funcţii membre:
-un constructor
-obţinerea datei curente
-modificarea datei curente
-incrementarea datei curente
-afişarea datei curente.
Se va defini o functie prieten pentru citirea datei curente.
Se vor defini de asemeni funcţii nemembre cu 2 parametri date calendaristice:
- Anterior care întoarce rezultatul boolean true/false după cum data argument 1 este înaintea sau
după data argument 2 şi
- Interval care întoarce numărul de zile cuprins între cele două date argumente.

14

S-ar putea să vă placă și