Din cauza moştenirii multiple se poate întâmpla ca o clasă de bază să fie prezentă în mai multe exemplare într-o clasă derivată. Să considerăm următorul exemplu:
Figura 1. Date membru moştenite în două exemplare
În acest caz datele membru ale clasei animal vor fi moştenite în două exemplare de către clasa câine. Primul exemplar se moşteneşte prin clasa domestic iar cel de al doilea prin clasa mamifer. Aceste date membru pot fi accesate folosind operatorul de rezoluţie precedată de numele clasei prin care se face moştenirea. Fişierul animal1.cpp:
Observăm că data membru nume s-a moştenit în două exemplare de către clasa câine, şi referirea la aceste date s-a făcut prin mamifer::nume respectiv domestic::nume. Prin execuţie se va afişa data membru nume de două ori, moştenit prin cele două clase. Deasemenea se va afişa greutatea şi comportamentul câinelui, şi faptul că latră sau nu.
Ar fi de dorit însă ca numele să fie memorat numai într-un singur exemplar în obiectele clasei câine. Acest lucru se poate realiza cu ajutorul claselor virtuale.
Dacă nu se doreşte ca datele membru a unei clase de bază să fie prezente în mai multe exemplare într-o clasă derivată, atunci se folosesc clase virtuale. Clasele de bază devin virtuale prin moştenire, dacă se specifică acest lucru prin plasarea cuvântului cheie virtual în faţa numelui clasei, în lista claselor de bază. Astfel clasa de bază respectivă va deveni virtuală referitor la clasa derivată.
Exemplul de mai sus se modifică în modul următor. Fişierul animal2.cpp:
Într-o ierarhie complicată de clase unele clase de bază pot fi moştenite în mai multe exemplare într-o clasă derivată. Între aceste exemplare pot fi virtuale şi nevirtuale. Să considerăm următorul exemplu referitor la acest lucru :
Figura 2. Ierarhie de clase
În această ierarhie de obiecte clasa de bază A se moşteneşte în mod virtual de către clasele B şi C şi în mod nevirtual de către clasa D. Clasele de bază ale clasei G sunt B, C, D, E şi F, toate moştenirile fiind nevirtuale cu excepţia clasei F. Se pot pune atunci următoarele probleme. În ce ordine vor fi executate constructorii în cazul în care se crează un obiect al clasei G? Dacă o clasă de bază este moştenită în mai multe exemplare într-o clasă derivată printre care pot fi atât virtuale cât şi nevirtuale, atunci de câte ori se va apela constructorul clasei de bază? Pentru a da răspuns la aceste întrebări să considerăm următorul program. Fişierul virtual2.cpp:
Prin execuţie se obţine următorul rezultat:
Acest rezultat se datorează următoarelor reguli.
-
În cazul în care se crează un obiect al clasei derivate, mai întâi vor fi executate constructorii claselor de bază virtuale în ordinea din lista_claselor_de_baza (vezi declaraţia claselor derivate), apoi constructorii claselor de bază nevirtuale în ordinea din lista_claselor_de_baza.
-
Dacă într-o ierarhie de clase, o clasă de bază se moşteneşte în mai multe exemplare într-o clasă derivată, atunci la crearea unui obiect al clasei derivate se va executa constructorul clasei de bază o dată pentru toate exemplarele virtuale, şi încă de atâtea ori câte exemplare nevirtuale există.
Deci în cazul exemplului de mai sus, mai întâi se execut constructorii claselor A şi F (ele fiind virtuale), apoi constructorii claselor B, C, D şi E (clase nevirtuale). Constructorul clasei D apelează mai întâi constructorul clasei A (ea fiind nevirtuală de această dată). De aceea ordinea de apelare a constructorilor va fi: A, F, B, C, A, D, E şi G.
17.5. Clase abstracte. Funcţia membru virtuală pură
În cazul unei ierarhii de clase mai complicate, clasa de bază poate avea nişte proprietăţi generale despre care ştim, dar nu le putem defini numai în clasele derivate. De exemplu să considerăm ierarhia de clase din figura 3.
Observăm că putem determina nişte proprietăţi referitoare la clasele derivate. De exemplu greutatea medie, durata medie de viaţă şi viteza medie de deplasare. Aceste proprietăţi se vor descrie cu ajutorul unor funcţii membru. În principiu şi pentru clasa animal există o greutate medie, durată medie de viaţă şi viteză medie de deplasare. Dar aceste proprietăţi ar fi mult mai greu de determinat şi ele nici nu sunt importante pentru noi într-o generalitate de acest fel. Totuşi pentru o tratare generală ar fi bine, dacă cele trei funcţii membru ar fi declarate în clasa de bază şi redefinite în clasele derivate. În acest scop s-a introdus noţiunea de funcţie membru virtuală pură.
Figura 3. Ierarhie de clase referitoare la animale
Funcţia virtuală pură este o funcţie membru care este declarată, dar nu este definită în clasa respectivă. Ea trebuie definită într-o clasă derivată. Funcţia membru virtuală pură se declară în modul următor. Antetul obişnuit al funcţiei este precedată de cuvântul cheie virtual, şi antetul se termină cu = 0. După cum arată numele şi declaraţia ei, funcţia membru virtuală pură este o funcţie virtuală, deci selectarea exemplarului funcţiei din ierarhia de clase se va face în timpul execuţiei programului.
Clasele care conţin cel puţin o funcţie membru virtuală pură se vor numi clase abstracte.
Deoarece clasele abstracte conţin funcţii membru care nu sunt definite, nu se pot crea obiecte aparţinând claselor abstracte. Dacă funcţia virtuală pură nu s-a definit în clasa derivată atunci şi clasa derivată va fi clasă abstractă şi ca atare nu se pot defini obiecte aparţinând acelei clase.
Să considerăm exemplul de mai sus şi să scriem un program, care referitor la un porumbel, urs sau cal determină dacă el este gras sau slab, rapid sau încet, respectiv tiner sau bătrân. Afişarea acestui rezultat se va face de către o funcţie membru a clasei animal care nu se supraîncarcă în clasele derivate. Fişierul abstract.cpp:
Observăm că deşi clasa animal este clasă abstractă, este utilă introducerea ei, pentru că multe funcţii membru pot fi definite în clasa de bază şi moştenite fără modificări în cele trei clase derivate.
Dostları ilə paylaş: |