C++ este unul dintre putinele limbaje de programare care accepta mostenire multipla, adica o clasa mosteneste mai multe clase. Clasele mostenite se enumera separate prin virgula dupa caracterul :
, specificand in fata fiecarei clase modul in care acea clasa este mostenita: public
, protected
, private
si optional specificatorul virtual
.
class C: public A, public B
{
...
C(...):A(...),B(...)
{...}
};
Inainte de crearea obiectul instanta C
sunt create subobiectele instanta A
si apoi celei instanta B
.
Mostenirea multipla vine si cu probleme suplimentare. Problema cea mai cunoscuta este cea a "Diamantului" care apare atunci cand o clasa mosteneste mai multe clase si cel putin doua din cele mostenite au stramos comuni. La instantiere, facilitatile stramosului ajung in obiectul creat pe doua cai. De fapt, obiectul creat va avea doua subobiecte instanta stramosului.
class S
{
public:
void f(){cout << "f" << endl;}
}
class A: virtual public S
{};
class B: virtual public S
{};
class C: public A, public B
{};
void main()
{
C c;
}
La instantierea clasei C
o data cu obiectul c
apar doua obiecte instanta A
, respectiv B
si pentru fiecare dintre aceste ultime doua obiecte cate un subobiect S
, adica in total 5 obiecte in memorie. Problema apare atunci cand din obiectul instanta C
incercam sa apelam/utilizam o facilitate mostenita din clasa S
, fie ea camp sau functie, adica, in exemplul nostru, problema apare la apelul c.f();
. Din cauza ca in memorie exista doua subobiecte instanta S
, compilatorul nu poate decide din care aceste subobiecte va apela functia f()
, rezultand eroare la compilare.
Solutia consta in a mosteni clasa S
virtual. Se spune astfel ca S
este o clasa virtuala in ierarhie. In urma mostenirii virtuale a clasei S
, la instantierea clasei C
se va creea un singur subobiect S
si in consectinta apelul c.f();
nu mai genereaza eroare la compilare.
Exista posibilitatea declararii unei clase in interiorul altei clase.
class A
{
...
class B
{...};
...
};
In exemplul de mai sus, spunem ca B
este imbricata in clasa A
.
Clasa B
nu are acces la membrii protejati sau privati din clasa A
si nici invers.
O clasa o descriem imbricata in alta clasa in general atunci cand numai in implementarea clasei mari este necesara existenta unor obiecte instanta clasei imbricate. Putem insa instantia clasa imbricata din exterior folosind operatorul de rezolutie:
A::B b;
class C
{
...
class B
{...};
...
};
Cele doua clase, desi au acelasi nume, sunt diferite (B
din A
si B
dn C
).
C::B
Pe langa functii template, C++ ofera ca suport de programare generica si clase template pentru care unul sau mai multe tipuri de date sunt generice (nespecificate atunci cand descriem clasa).
Inaintea unei clase template trebuie sa apara acea specificatie pe care am intalnit-o la functiile template si anume, cuvantul rezervat template
, urmat de o lista de date generice, specificata intre <
si >
. In fata fiecarui tip generic, apare tipul rezervat class
.
template <class T1, class T2,...>
class A
{...};
Pentru a nu avea probleme la link-editare in Visual C++ trebuie sa implementam toate metodele in momentul descrierii clasei, ci nu separat in fisier .cpp
.
Daca in cazul functiilor template nu era nevoie la apel sa indicam tipurile de date ce substituie tipurile generice, identificarea lor facandu-se pe baza tipurilor de date ale parametrilor, in cazul claselor template trebuie sa specificam de fiecare data, cand ne referim la ele, intre <
si >
tipurile de date ce inlocuiesc tipurile generice.
De exemplu, pentru clasa A
, vom scrie:
A<int,float,...>
(A de int
float
...
)
template <class T>
class Punct3D
{
T x, y, z;
public:
Punct3D(T X, T Y, T Z)
{x = X; y = Y; z = Z;}
T abs(){return x*x + y*y + z*z;}
};
void main()
{
Punct3D<int> p1(1,2,3);
cout << p1.abs() << endl;
Punct3D<float> p2(1,0.5,2);
cout << p2.abs() << endl;
}
Toate functiile membre unei clase template devin la randul lor functii template.
ios
- input output stream standard
Ierarhia este pornita de clasa ios
.
Clasa ios
porneste ierarhia, contine o serie de facilitati comune intregii ierarhii, in schimb nu se instantiaza direct. Clase aflate pe pozitii mai jos se instantiaza efectiv pentru a prelucra fluxul.
Ierarhia ios
foloseste instante de obiecte ale ierarhiei streambuf
atunci cand fluxurile sunt prelucrate. Un obiect instanta din ierarhia streambuf
gestioneaza buffer-ul atasat fluxului.
Din cauza ca atunci cand prelucram un flux prin intermediului unui obiect instanta clasei ios
si implementarea claselor a fost facut incat un obiect streambuf
este gestionat automat, nu este necesar sa cunoastem membrii claselor streambuf
, fiind suficient a cunoaste membrii claselor ios
. Din clasa streambuf
sunt derivate clasele filebuf
(fiind specializata pe buffere atasate fisierelor) si strstreambuf
(fiind specializata pe buffere pentru stringuri).
Clasa ios
are doi constructori: ios()
si ios(streambuf* buf)
.
Clasa ios
contine urmatoarele campuri:
adjustfield
: este un camp constant static transmis de obicei in al doilea parametru al functieisetf
; aceasta constanta este folosita pentru curatatea bitilor de formatare legati de aliniere (left
sauright
).basefield
: este un camp static constanta de tiplong
si este folosit pentru caratarea bitilor de formatare pentru bazele de numeratie.floatfield
: constanta statica de tiplong
pentru curatarea bitilor de formatare cu virgula mobila (reale).bp
: memoreaza adresa buffer-ului atasat fluxului, adica obiectul instantastreambuf
.state
: este o valoare de tipint
ce memoreaza starea fluxului; bitii acestui camp daca sunt1
inseamna ca au aparut erori in prelucrarea fluxului si dau informatii cu privire la tipul erorii.x_fill
: o valoare de tipint
care este folosit pentru caracterul pentru umplerea spatiilor liberex_flags
: o valoare de tiplong
ce retine bitii de formatare la scriere (aliniere, baze de numeratie, etc.)x_precision
: o valoare de tiplong
care retine precizia de afisare in virgula mobila a numerelor reale, adica numarul de cifre exacte folosite la afisare.x_width
: valoareint
ce retine numarul de caractere pe care se face afisarea.
Campurile care nu sunt constante de mai sus nu sunt publice. Aceste campuri se modifica folosind functii ale clasei ios
sau ale claselor derivate din ios
.
Functiile membre clasei ios
:
int bad()
: returneaza o valoare nenula atunci cand in prelucrarea fluxului au aparut erori; sunt interogati bitiiios::badbit
siios::hardfail
.int fail()
: returneaza o valoare nenula daca au aparut erori in prelucrarea fluxului, interogandu-se in plus fata deint bad()
si bitulios::failbit
.int good()
: returneaza1
daca nu au aparut erori in prelucrarea fluxului.void clear(int st=0)
: aduce fluxul din nou in stare buna; campulstate
al parametrului, implicit0
, evident cand nu exista erori.char fill()
: returneaza caracterul folosit pentru caracterul care umple spatiile libere, adica returneaza valoarea luix_fill
.char fill(char c)
: seteaza noul caracter al luix_fill
cu valoarea luic
si returneaza vechiul caracter alx_fill
.long iosflags()
: returneaza valoare campuluix_flags
.long iosflags(long f)
: seteaza noua valoare a luix_flags
cu valoarea luif
si returneaza vechea valoare a luix_flags
.void init(streambuf* buf)
: specificam adresa unui buffer ce se ataseaza fluxului curent.int precision()
: returneaza precizia de afisare a numerelor reale.int precision(int p)
: seteaza preciziax_precision
la valoarea luip
si se returneaza vechea valoare a luix_precision
.stringbuf* rdbuf()
: returneaza adresa catre buffer-ul fluxului.int rdstate()
: returneaza valoarea luistate
.int setstate(int s)
: campulstate
devines
.long setf(long flags
: bitii1
ai variabileix_flags
fac sa devina biti1
pe pozitiile corespunzatoare in campulx_flags
si se returneaza vechea valoare a campuluix_flags
.long setf(long flags,long field)
: reseteaza bitii (ii face 0) dinx_flags
pe pozitiile indicate de bitii cu valoare1
ai parametruluifield
si seteaza bitii dinx_flags
la valoarea1
pe pozitiile in care bitii sunt1
in parametrulflags
, returnand valoarea campuluix_flags
inainte de modificare.long unsetf(long l)
: bitii1
ai variabileil
anuleaza setari din campulx_flags
.void sync_width_stdin()
: fluxulios
este sincronizat cu fluxul standard de intrare din C; sincronizarea incetineste executia.ostream* tie()
: este returnata adresa fluxului legat de fluxul curent; de exemplu, fluxulcin
este legat decout
.ostream* tie(ostream* f)
: fluxul curent este legat de adresa data ca parametru, returnandu-se vechea legatura.int width()
: returneaza valoarea campuluix_width
.int width(int w)
: este setata lungimea la afisarea formatata, returnandu-se vechea lungime.