Sunteți pe pagina 1din 33

Programare obiect-orientată (POO)

CURS 1. INTRODUCERE
Cuprins

• Introducere;
• Organizarea cursului POO. Procedura de evaluare;
• Paradigme de programare;
• Evoluţia limbajelor de programare orientate obiect;
• Obiect, mesaje, metode;
• Comparaţie Smalltalk versus C++;
• Concluzii.

Obiectivele învăţării

După acest curs, veţi putea:


— Să prezentaţi un scurt istoric al limbajelor de programare orientate pe obiecte;
— Să definiţi termenii: obiect, metodă, mesaj, selector;
— Să prezentaţi cel puţin patru carateristici ale limbajului de programare
Smalltalk.

Introducere

POO este o continuare a PCLP1 și PCLP2.


— Teme abordate la POO*:
¡ Limbaje orientate pe obiecte – evoluţie, caracteristici;
¡ Paradigme de programare; ¡ Principiile programării orientate pe obiecte;
¡ Bune practici pentru realizarea aplicaţiilor;
¡ Arhitectura MVC;
* ordinea poate să difere

Organizarea cursului POO

— Curs: Conf. dr. ing. Mihai Bîzoi, A-102


— Laborator: Conf. dr. ing. Mihai Bîzoi, A-107
¡ Curs->toţi de la ELA+TST II, 2h săptămânal
¡ Laborator->pe subgrupe (3), 2h la 2 săptămâni
— Se finalizează cu examen.
— În laborator se va folosi mediul de programare Code Blocks.
Procedura de evaluare
— 1 punct din oficiu;
— 5 puncte Laborator;
¡ 1 punct activitatea de laborator (prezenţa);
¡ 1.5 puncte test grilă; ¡ 2.5 puncte colocviu de laborator;
— 4 puncte Examen.
— Se pot prezenta la examen numai studenţii care au evaluare în laborator.
— Examenul va fi scris.

Paradigme de programare

O paradigmă de programare este un anumit stil de programare care reprezintă o


anumită filosofie.
— Principalele paradigme de programare:
¡ Paradigma funcţională. Aceste limbaje pun accent pe aplicarea unei funcţii (de
multe ori recursivă) la un set de una sau mai multe elemente de date. Funcţia va
întoarce o valoare - rezultatul de evaluare al funcţiei. Abordarea funcţională s-a
dovedit a fi de mare folos pentru cercetătorii din domeniul inteligenţei artificiale.
Lisp fiind un exemplu clasic al unui astfel de limbaj.

¡ Paradigma procedurală. Este exemplificată de limbaje de programare cum ar fi


Pascal sau C. Astfel au fost accentuate soluţiile algoritmice şi procedurile care
operează pe elemente de date. Acestea au condus la creşterea în complexitate a
sistemelor dezvoltate.

¡ Paradigma modulară. În aceste limbaje, un modul ascunde datele sale de


modulele utilizatorilor. Utilizatorii modulului pot accesa datele numai prin
interfeţele definite. Aceste interfeţe sunt publicate pentru ca utilizatorii să
cunoască ce interfeţe sunt disponibile (inclusiv definiţiile lor) şi să poată verifica
daca folosesc versiunile corecte.

¡ Paradigma orientată obiect. Această abordare poate fi considerată următorul


pas după modularizare. Nu numai că există module explicite (în acest caz
obiectele) dar aceste obiecte pot moşteni comportamentul altor obiecte.
Evoluţia limbajelor POO

— Idei teoretice => începând cu anii ’50;


— Termenul de obiect a fost introdus prima dată de limbajul Simula 67 (anii ’60);
Simula a introdus noţiunile: clasă și instanţă.
— “programare orientată pe obiecte” => limbajul Smalltalk (anii ’70); Clasele
puteau fi create și modificate dinamic;
— Smalltalk 80 => primul limbaj de programare orientat obiect pur;
— Limbaje de programare orientate obiect pure (totul este obiect): Smalltalk,
Eiffel (1985), Ruby (anii ’90); — Ruby are sintaxa inspirată din Perl și Smalltalk, cu
influenţe din Eiffel și Lisp;
— Limbaje de programare hibride: Java, C++, C#, Python, CLOS (Common Lisp
Object System), Perl, PHP, Visual Basic;

Ce este obiectul

Obiectul este conceptul fundamental care stă la baza programării orientate pe


obiecte. Un obiect este o combinaţie a două componente:
¡ date – reprezintă starea unui obiect;
¡ operaţii – toate mecanismele de accesare şi manipulare a stării obiectului.

Metode

Metodele sunt similare cu subrutinele, funcţiile sau procedurile din alte limbaje
de programare. Acestea sunt fragmente de cod identificate printr-un nume, care
pot fi invocate individual şi care returnează o valoare când se termină de rulat.
— Metodele pot fi apelate pe baza unui selector.
Mesaje

— Limbajele de programare orientate pe obiecte folosesc trimiterea de mesaje ca


fiind singura posibilitate de a efectua operaţii.
— Dacă obiectul destinatar înţelege mesajul care i-a fost transmis, atunci una din
operaţiile sale (sau metode) va fi efectuată. În aceste condiţii se vor efectua
anumite calcule şi întotdeauna va fi returnat un rezultat (de exemplu un obiect).
— Trebuie remarcat faptul că un mesaj specifică doar ce operaţii sunt cerute şi nu
cum trebuie acestea effectuate.

• Deoarece starea unui obiect este privată şi nu poate fi accesată în mod direct din afara
lui, singura posibilitate de a accesa starea acestuia este de a transmite un mesaj
obiectului.

• Setul de mesaje la care răspunde un obiect se numeşte interfaţă mesaj.


Concluzii
— Paradigma de programare orientată obiect a evoluat din paradigma modulară;

— Limbajele POO au evoluat începând cu anii ’60, în prezent există o largă varietate de
limbaje pur orientate obiect sau hibride;

— Obiectul este conceptul fundamental care stă la baza programării orientate pe


obiecte;

— În esenţă, un program al unui limbaj orientat obiect reprezintă o serie de expresii


prin care se transmit mesaje obiectelor.
Întrebări
— Cum este tipul variabilelor la limbajul C++?

— În ce condiţii, o metodă a unui obiect este evaluată?

— Ce este selectorul? — Puteţi face o comparaţie între termenii: mediu de programare și


limbaj de programare?

— Prezentaţi câte patru caracteristici ale limbajelor Smalltalk și C++.


CURS 2. CLASE ȘI INSTAN Ț E

Cuprins
— Introducere;

— Mesaje între obiecte;

— Clase şi instanţe;

— Evaluarea metodelor;

— Principiile programării orientate pe obiecte;

— Concluzii.

Obiectivele învăţării

După acest curs, veţi putea:

— Să definiţi termenii clasă și instanţă;

— Să prezentaţi modul de evaluare al metodelor;

— Să prezentaţi caracteristicile încapsulării;

— Să descrieţi la modul general ce este moștenirea;

— Să definiţi termenii abstractizare și polimorfism.

Introducere

— Obiectul este conceptul fundamental care stă la baza programării orientate pe


obiecte. Un obiect este o combinaţie a două componente: date şi operaţii.

— Metodele sunt fragmente de cod identificate printr-un nume, care pot fi invocate
individual şi care returnează o valoare când se termină de executat.

— Limbajele de programare orientate pe obiecte folosesc trimiterea de mesaje ca fiind


singura posibilitate de a efectua operaţii.
— Dacă obiectul destinatar înţelege mesajul care i-a fost transmis, atunci una din
metodele sale va fi executată.

Mesaje între obiecte


— În teorie, un programator poate implementa un obiect în termenii variabilelor
conţinute şi a setului de mesaje la care răspunde sau îl înţelege.

— Oricum, este mult mai util să partajăm informaţii între obiecte similare.

— Această abordare permite nu numai ocuparea unui spaţiu mai mic de memorie, dar
şi posibilitatea de a reutiliza codul.

Clase şi instanţe
— Informaţia este partajată prin gruparea acelor obiecte care reprezintă acelaşi tip de
entitate într-o structură numită clasă.

— Într-un limbaj de programare orientat pe obiecte, orice obiect este membru al unei
singure clase, acesta purtând numele de instanţa acelei clase.

— De asemenea, orice obiect conţine o referinţă a clasei căreia îi este instante.


— Clasa unui obiect se comportă ca un şablon pentru a determina numărul variabilelor
interne pe care le va avea instanţa şi păstrează o listă a metodelor care corespund
mesajelor la care toate instanţele clasei vor răspunde.

— Această relaţie dintre clase şi instanţe semnifică faptul că obiectele clase combină
două tipuri de cod şi date. Astfel, este codul şi datele pe care clasa obiect îl conţine în
sine (cunoscute sub numele metodele clasei şi variabilele clasei) dar şi un şablon pentru
codul şi datele pe care instanţa clasei îl va conţine (cunoscute sub numele de metodele
instanţei şi variabilele de instanţă).

— Obiectele clasă înţeleg numai metodele clasei, iar obiectele instanţă înţeleg numai
metodele instanţei.

— Programatorul trebuie să fie foarte atent deoarece foarte des pot apărea confuzii.
Dacă se aşteaptă ca mesajele transmise obiectelor să fie înţelese, atunci trebuie transmise
claselor - mesaje de clasă şi instanţelor - mesaje de instanţă.

— Orice instanţă dintr-o clasă particulară are propriul set de variabile de instanţă
definit în acea clasă. Acestea nu sunt partajate între instanţe.

Variabile de instanţă / clasă

— Dacă o clasă defineşte o variabilă de instanţă numită cont, orice instanţă a acelei clase
va avea o variabilă separată numită cont.

— Valoarea acestei variabile - cont, va fi probabil diferită între instanţe.

— Situaţia variabilelor de clasă diferă faţă de ce a fost prezentat mai sus.

— Variabilele de clasă sunt definite în clasă şi sunt vizibile şi partajate între toate
instanţele acelei clase.
Exemplu C++ (1)

Exemplu C++ (2)


Exemplu Ruby

Ierarhizarea claselor (Smalltalk)


Evaluarea metodelor (în paşi)

1. Un mesaj este transmis unui obiect care este instanţa unei anumite clase.

2. Se face o căutare în dicţionarul metodelor clasei respective pentru metoda


corespunzătoare mesajului selector.

3. Dacă metoda este găsită acolo, atunci aceasta este evaluată şi este întors un răspuns
corespunzător.

4. În cazul în care metoda corespunzătoare nu este găsită, atunci se va face o căutare în


instanţa clasei superclasă imediat următoare.

5. Acest proces se repetă în ierarhia claselor până când metoda este localizată sau nu
mai există nici o superclasă.

6. În ultimul caz, sistemul anunţă programatorul că a apărut o eroare la rulare.

Principii ale programării orientate pe obiecte

Încapsularea sau ascunderea datelor

— Încapsularea = procesul ascunderii tuturor detaliilor unui obiect care nu contribuie


la caracteristicile lui esenţiale.

— În esenţă, ce se află în interiorul clasei este ascuns.

— Alte obiecte cunosc doar interfaţa externă.


— Avantaj: utilizatorul datelor nu trebuie să cunoască cum, unde şi în ce formă sunt
stocate datele.

— Limbajele de programare orientate pe obiecte furnizează facilităţi de încapsulare prin


prezentarea utilizatorului unui obiect a unui set de interfeţe externe.

Moştenirea

— În multe cazuri obiectele pot avea proprietăţi similare (dar nu identice).

— O metodă de a clasifica aceste proprietăţi este de a crea o ierarhie a claselor.

— În această ierarhie clasa moşteneşte atât clasa părinte aflată superior în ierarhie cât şi
clasa superioară clasei părinte.

— Acest mecanism de moştenire permite definirea o singură dată a caracteristicilor


comune ale obiectelor care sunt folosite în locuri diferite.

— Moştenirea ne permite să afirmăm că o clasă este similară cu altă clasă, dar cu un


anumit set de diferenţe.

— O altă modalitate de aplicare a acesteia poate fi considerată posibilitatea de a defini


toate lucrurile care sunt comune despre o clasă de lucruri şi apoi definirea a ceea ce este
special despre fiecare grupare în cadrul unei subclase.

Abstractizarea
— Abstractizarea = caracteristica esenţială a unui obiect de a se distinge faţă de toate
celelalte obiecte şi care furnizează graniţe conceptuale clare, relative din perspectiva
utilizatorului.

— Abstractizarea = starea prin care un obiect diferă de toate celelalte.

— În anumite limbaje de programare, abstractizarea este considerată în legătură cu


protecţia datelor. De exemplu:

¡ limbajele C++ şi Java au posibilitatea de a stabili dacă o subclasă poate suprascrie date
sau proceduri.

¡ Smalltalk nu furnizează posibilitatea de a stabili dacă o procedură poate fi suprascrisă,


dar permite programatorului să stabilească dacă o procedură (sau metodă) este
responsabilitatea unei subclase.
Polimorfismul
— Polimorfismul = abilitatea de a transmite acelaşi mesaj unor instanţe diferite care par
să îndeplinească aceleaşi funcţii.

— Modul în care mesajul este înţeles diferă însă de clasa fiecărei instanţe.

— În programarea orientată pe obiecte există două tipuri de polimorfism: supraîncărcat


şi superior.

— Diferenţa dintre cele două nume se referă la modul în care mecanismul folosit
determină ce cod este executat.

— Polimorfismul supraîncărcat apare când procedurile au acelaşi nume dar sunt


aplicate unor tipuri de date diferite. Compilatorul poate prin urmare să determine care
operator va fi utilizat la momentul compilării şi dacă poate folosi versiunea corectă a
operatorului.

— Este considerat polimorfism superior când procedura este definită atât în clasă cât şi
în subclasă. Aceasta înseamnă că atât clasa cât şi subclasa vor răspunde la cererea
pentru această procedură (considerând că nu a fost stabilită ca privată pentru clasă).

Concluzii
— Informaţia este partajată prin gruparea acelor obiecte care reprezintă acelaşi tip de
entitate într-o structură numită clasă.

— Într-un limbaj de programare orientat pe obiecte, orice obiect este membru al unei
singure clase, acesta purtând numele de instanţa acelei clase.

— Clasa unui obiect se comportă ca un şablon pentru a determina numărul variabilelor


interne pe care le va avea instanţa şi păstrează o listă a metodelor care corespund
mesajelor la care toate instanţele clasei vor răspunde.

— Variabile de instanţă nu sunt partajate între instanţe.

— Variabilele de clasă sunt definite în clasă şi sunt vizibile şi partajate între toate
instanţele acelei clase.
Întrebări
— Poate fi definită o clasă ca fiind un grup de obiecte?

— Realizaţi o analogie cu obiecte din viaţa reală pentru a explica semnificaţia termenilor
clasă și instanţă. — Ce tip de moștenire este implementată în limbajul Smalltalk?

— Care sunt obiectele care primesc mesaje? Exemplificati.


CURS3.CONSTRUCTORIȘIDESTRUCTORI

Cuprins

 Introducere;

 Constructori;

 Caracteristici constructori;

 Tipuri de constructori;

 Destructori;

 Caracteristici destructori;

 Concluzii.

Obiectivele învățării
După acest curs, veți putea:

 Să prezentați rolul și caracteristicile constructorilor în limbajul C++.

 Să prezentați tipurile de constructori.

 Să prezentați rolul și caracteristicile destructorilor.

Introducere

 În cazul variabilelor obișnuite, compilatorul asigură alocarea spațiului de memorie și


eventual inițializarea explicită cu valori inițiale în declarație. Pentru variabilele
dinamice, compilatorul C nu dispune de nici o metoda de inițializare și nici operatorul
new nu rezolvă toate situațiile. În acest caz, rămâne în grija programatorului atribuirea
de valori adecvate datelor înainte de utilizare.

 Această abordare este nesatisfacătoare în multe situații în cazul obiectelor. Pentru


crearea, inițializarea, copierea și respectiv distrugerea obiectelor, în C++ se folosesc
funcții speciale, numite constructori și destructori.
 Constructorul se apelează automat la crearea fiecărui obiect al clasei, static, automatic
sau dinamic (cu operatorul new), inclusiv pentru obiecte temporare.

 Destructorul este apelat automat la eliminarea unui obiect, la încheierea timpului sau
de viață, sau poate fi solicitat prin program, cu operatorul delete.

Constructor – Definiție

 Constructorul este o funcție membră specială a unei clase ce se apelează în mod


automat la crearea unui obiect

 Rol:

 alocare

 inițializare

Constructor – Sintaxă
Exemplu

Constructori - Caracteristici

 au același nume cu cel al clasei din care fac parte.

 nu returnează nimic (nici macar tipul void).

 o clasă poate avea mai mulți constructori.

 nu pot primi ca parametri instanțe ale clasei ce se definește, ci doar pointeri sau
referințe la instanțele clasei respective.

 constructorii nu sunt apelaţi explicit (în general).

 constructorii nu se moștenesc.

 constructori nu pot fi funcții virtuale.

Tipuri de constructori

 Constructori impliciți

 definit de utilizator – constructor ce nu are niciun parametru


 generat de compilator – daca o clasă nu are niciun constructor definit atunci
compilatorul generează unul automat, fară parametri al cărui corp nu conține nicio
instrucțiune

 constructor cu toți parametri impliciți

 Constructori cu parametri

 cu parametri ce nu iau valori implicite

 cu parametri ce iau valori implicite

 Constructori de copiere – iniţializarea obiectelor din altele deja existente.

 definiți de utilizator

 generați de compilator

Exemplu – constructor implicit

Exemplu: Constructor cu parametri impliciți


Exemplu: Constructor cu parametri

Constructori de copiere

 Sintaxa:

 Utilizare

 Crearea de obiecte cu iniţializare, pornind de la un obiect care există (IdClasa


ob2=ob1).
 Apelul unei funcţii care lucrează cu obiecte transferate prin valoare, când este nevoie
de crearea unei copii a obiectului pe stivă (cazul f(ob);).

 Returnarea dintr-o funcţie a unui obiect prin valoare (return ob;)

Exemplu: Constructor de copiere (I)

Exemplu: Constructor de copiere (II)


Destructori

 Destructorul este o funcție membră specială a unei clase ce apelează în mod automat
distrugerea unui obiect

 Rol: eliberarea zonelor alocate dinamic, resurselor, etc.

 Tipuri:

 Definit de utilizator

 Generat de compilator

Destructor – Sintaxă

Exemplu: Destructor (I)


Exemplu: Destructor (II)

Destructori - Caracteristici

 Are același nume cu numele clasei și este precedat de ~

 Nu are parametri

 Nu returnează nimic (nici măcar void)

 O clasa poate avea un singur destructor

 Pot fi funcții virtuale

Concluzii

Constructorii și destructorii se declară și se definesc similar cu celelalte funcții membre,


dar se disting de acestea printr-o serie de caracteristici specifice:

 numele funcțiilor constructor sau destructor coincide cu numele clasei căreia îi


aparțin; destructorii au numele precedat de caracterul (~).

 în declarație și definiție nu se specifică nici un tip de rezultat, nici macar void.

 constructorii pot avea parametrii, inclusiv parametrii impliciți și pot fi supradefiniți;


destructorii nu au aceste proprietăți.

 nu se pot utiliza pointeri către constructori sau destructori.

 dacă o clasă nu dispune de constructori și destructori definiți, compilatorul va genera


automat un constructor implicit, respectiv un destructor, funcții publice.
Întrebări

 Ce operator apelează un constructor? Dar un destructor?

 Care este rolul constructorului? Dar al destructorului?

 Ce tipuri de constructori cunoașteți?

 De ce nu ați creat constructori și destructori în programele create în laborator?


CURS4.MOȘTENIREAÎNLIMBAJULC++

Cuprins 2

 Introducere;

 Avantajele moștenirii;

 Sintaxa definiri claselor derivate;

 Accesul asupra membrilor moșteniți;

 Tipuri de moștenire;

 Constructori / destructori în procesul de moștenire;

 Concluzii.

Obiectivele învățării
După acest curs, veți putea:

 Să prezentați proprietatea de moștenire la C++ și să enunțați avantajele oferite de


aceasta;

 Să enunțați ce membrii se moștenesc și cum se face accesul la aceștia;

 Să definiți tipurile de moștenire;

 Să explicați rolul constructorilor și al destructorilor în procesul de moștenire.

Introducere

 Moștenirea este mecanismul prin care o clasă preia structura (datele membru) şi
comportamentul (metodele) unei alte clase la care adaugă elemente specifice.

 În limbajul C++ este implementat conceptul de moştenire.

 Stabileste o relatie de genul "este un/este o" (is-a).

 Clasă de baza = clasa de la care se preia structura şi comportamentul.

 Clasa derivată = clasa care preia structura şi comportamentul.


Moștenirea claselor

Avantaje

 Posibilitatea reutilizării codului;

 Obţinerea extensiei unei clase fără a fi necesară o recompilare a clasei inițiale;

 Utilizarea polimorfismului în timpul executiei programului prin folosirea funcțiilor


virtuale.

Exemplu
Ce se moşteneşte?

 În principiu, fiecare dată sau funcţie membru a clasei de bază se moşteneşte în clasa
derivată:

 diferă doar protecţia acestora

 Există şi excepţii:  Constructorii şi destructorii

 Supraîncărcarea operatorului =

 Aceştia nu se moştenesc, fiind metode specifice clasei.

Sintaxa definirii unei clase derivate

 Sintaxa definirii unei clase derivate este următoarea: class IdClasaDerivata:


modif_acces1 IdClasaB1, …, modif_accesN IdClasaBn{ //date si metode specifice clasei
derivate }; unde:

 IdClasaDerivata este numele clasei derivate

 IdClasaB1, …, dClasaBn sunt clasele de bază de la care se moştenesc datele şi


metodele

 modif_acces1, …, modif_accesN sunt modificatori de acces: public, protected, private

Accesul asupra membrilor moşteniţi


Exemplul 1

Exemplul 2
Utilizarea datelor şi metodelor moştenite

Tipuri de moştenire

 Moştenire simplă – clasa derivată preia caracteristicile şi metodele unei singure clase
de bază;

 Moştenire multiplă - clasa derivată preia caracteristicile şi metodele de la mai multe


clase de bază;

Moştenire multiplă. Exemplu


Constructori în procesul de moştenire

 Dacă o clasă D este derivata din clase (B1, …, Bn) atunci constructorul clasei B va
avea suficienţi parametri pentru a iniţializa datele membru ale claselor B1,…, Bn.

 La crearea unui obiect al clasei D se vor apela mai întâi constructorii claselor B1,…,Bn,
în ordinea specificată în lista de moştenire, pentru a se iniţializa datele membre ale
claselor de bază şi apoi se vor executa intrucţiunile constructorului clasei D.

Apelul constructorilor claselor de bază

 Explicit – Listă de inițializare;

 Implicit – dacă constructorul clasei derivate nu apelează explicit constructorul uneia


din clasele de bază atunci compilatorul va apela automat constructorul implicit al acelei
clase de bază.

Destructori în procesul de moştenire

 La distrugerea unui obiect al clasei derivate compilatorul va executa mai întâi


destructorul clasei derivate şi apoi destructori claselor de bază în ordinea inversă
apelului constructorilor.
Exemplu

Exemplu

Constructorul de copiere în procesul de moştenire

 Dacă clasa derivată nu are definit un constructor de copiere atunci compilatorul


generează unul automat care va realiza copierea datelor membre moştenite apelând la
constructorii de copiere ai claselor de bază (definiţi de utilizator sau generaţi de
compilator), iar datele specifice clasei derivate se vor copia bit cu bit.
 Dacă clasa derivată are definit un constructor de copiere atunci acesta se va realiza
copierea tuturor datelor membre (moştenite sau proprii) - apelând eventual la
constructorii claselor de bază.

Supraîncărcarea operatorului de atribuire

 Dacă clasa derivată nu are supraîncărcat operatorul de atribuire atunci compilatorul


va realiza copierea datelor membre moştenite apelând la supraîncărcările operatorului
de atribuire definte în clasele de bază (dacă există), iar datele membre specific vor fii
copiate bit cu bit.

 Dacă clasa derivată are supraîncărcat operatorul de atribuire atunci acesta va realiza
copierea tuturor datelor membre.

Exemplu – partea 1
Exemplu – partea 2

Exemplu – partea 3
Concluzii

 Moștenirea permite definirea de clase noi (clase derivate) reutilizând clasele existente
(clase de bază). Clasa nou creată moștenește comportamentul (metodele) și
caracteristicile (variabile membre, starea) de la clasa de bază.

 Dacă A și B sunt două clase unde B moșteneste de la clasa A (B este derivată din clasa
A sau clasa B este o specializare a clasei A) atunci:

 clasa B are toate metodele și variabilele membre din clasa A;

 clasa B poate redefini metodele din clasa A;

 clasa B poate adauga noi membrii (variabile, metode) pe lângă cele moștenite de la
clasa A.

Întrebări

 Ce reprezintă proprietatea de moștenire?

 De câte tipuri este moștenirea?

 Ce nu se poate moșteni o clasă derivată?

 În ce ordine se apelează constructorii? Dar destructorii?

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