Documente online.
Zona de administrare documente. Fisierele tale
Am uitat parola x Creaza cont nou
 HomeExploreaza
upload
Upload




Clase derivate si mostenire

c


Clase derivate si mostenire

Spunem ca clasa A este o clasa derivata din clasa B daca clasa A are "trasaturile" clasei B, eventual, si altele noi. Clasa B se mai numeste clasa de baza pentru A.



Exemplu: Clasa predefinita ifstream este o clasa derivata din clasa de baza istream.

Putem defini propriile noastre clase C++. O clasa derivata are toate functiile si variabilele membru ale clasei de baza, dar poate avea si functii membre suplimentare si/sau variabile membru suplimentare.

Sintaxa generala pentru declararea clasei derivate A este:

class A : lista_claselor_de_baza

;

Exemplu:

class A : B

;

class A: private B

class A: B, public C

;

Pentru situatia 3 clasa A este o clasa derivata a claselor B si C (Se poate defini derivare si pentru struct, dar nu si pentru union). Cuvintele rezervate private si public din exemplul preceden 111e48b t se numesc modificatori de protectie. Implicit, modificatorul de protectie este private la definirea unei clase derivate si public la definirea unei structuri derivate. Deci, cazurile 1 si 2 din exemplul precedent sunt practic identice.

Deci, modificatorii de protectie utilizati in lista_claselor_de_baza definesc protectia in clasa derivata a elementelor mostenite.

Accesul in clasa de baza

Modificatorul de protectie din lista_claselor_de_baza

Accesul in clasa derivata a elementului mostenit

Accesul din exterior

private

protected

public

private

private

private

inaccesibil

private

private

inaccesibil

inaccesibil

inaccesibil

private

protected

public

public

public

public

inaccesibil

protected

public

inaccesbil

inaccesbil

accesbil

Pentru a oferi clasei derivate acces la un membru al clasei de baza, acesta trebuie declarat protected sau public. Pentru respectarea principiului incapsularii datelor, datele membre pentru care se ofera acces claselor derivate se declara in clasa de baza cu atributul protected. De asemenea pentru a conserva dreptul de acces in urma derivarii, se utilizeaza modificatorul de acces public. Accesul poati fi stopat pe orice nivel al ierarhiei de clase printr-un modificator de acces private.

Este esential ca atributele de acces ale membrilor unei clase, precum si modificatorii de acces ale claselor derivate sa fie stabilite astfel incat sa permita dezvoltarea ierarhiei de clase fara a afecta incapsularea datelor. Controlul accesului trebuie utilizat cu atentie pentru a conserva avantajele de programarea orientata pe obiecte.

Relatia dintre constructorii si destructorii claselor de baza si ai clasei derivate

Constructorii si destructorii sunt functii membru care nu se mostenesc. La instantierea unui obiect al unei clase derivate se apeleaza atat constructorii clasei derivate cat si cei ai claselor de baza. Intai se apleleaza constructorii claselor de baza si la urma se apeleaza si constructorul clasei derivate. Ordinea de apel a constructorilor claselor de

baza corespunde cu ordinea in care sunt indicate clasele respective in "lista_claselor_de_baza" din definitia clasei derivate.

La distrugerea unui obiect al clasei derivate, destructorii se apeleaza in ordine inversa. Deci, intai se apeleaza destructorul clasei derivate si apoi se apeleaza destructorii claselor de baza in ordine inversa fata de ordinea de apel a constructorilor la instantierea obiectului respectiv.

Exemplu:

#include <iostream.h>

class baza

~baza()

};

class derivata : public baza

~derivata()

};

int main()

// La ecran va apare:

// Constructor al clasei de baza.

// Constructor al clasei derivate.

// Destructor al clasei derivate.

// Destructor al clasei de baza.

La instantierea unui obiect se transmit valori parametrilor constructorilor pentru initializarea datelor membru. In cazul unui obiect al unei clase derivate, o parte din valori se folosesc pentru initializarea datelor membru specifice clasei derivate, iar restul pentru initializarea datelor membru ale claselor de baza.

Constructorul clasei derivate contine parametri pentru toate valorile care se utilizeaza la initializarea obiectelor. Transferul valorilor de initializare pentru datele membru ale claselor de baza se defineste cu ajutorul antetului

constructorului clasei derivate.

Fie A o clasa derivata din clasele de baza B­­1, B2, ..., Bn:

class A: public B1(...), public B2(...), ..., public Bn

;

Constructorul A are un antet la definirea sa de forma:

A:: A(...) : B1(...), B2(...), ..., Bn(...)

Constructorul A are o lista de parametri completa, adica pentru initializarea tuturor detelor membru ale unui obiect de tip A.

Expresia Bi(...) (i=1,2,...,n) din antetul constructorului A contine, in paranteza, o lista de expresii care definesc valori initiale pentru datele membru ale clasei Bi.

Daca o clasa Bi nu are constructor, atunci pentru clasa respectiva nu va fi prezenta o expresie de forma Bi(...) in antetul constructorului A. De asemenea, expresia respectiva nu va fi prezenta in antetul lui A daca datele membru ale clasei Bi se initializeaza cu valori implicite sau clasa Bi nu are date membru.

Expresia Bi(...) este un apel explicit al unui constructor al clasei Bi. Ordinea in care se scriu expresiile Bi(...) in antetul constructorului A este arbitrara. Constructorii se apeleaza totdeauna in ordinea in care sunt scrise

clasele in "lista_de_clase_de_baza".

O conditie esentiala pentru a se putea apela constructorii claselor de baza prin intermediul clasei derivate este ca acestia sa aiba protectia de tip protected sau public.

Lista care defineste apelurile constructorilor claselor de baza nu este prezenta in prototipurile constructorilor claselor derivate, ci numai in antetele acestora. In general, clasele au constructori definiti de programatori. In cazul in

care o clasa nu are contructori, compilatorul genereaza automat un constructor implicit. (fara parametri) pentru clasa respectiva.

Teoretic, pot apare urmatoarele 4 cazuri:

Clasa derivata si clasele de baza ale ei nu au constructori definiti de programator;

Cel putin o clasa dintre clasele de baza are constructori, iar clasa derivata nu are constructori;

Clasa derivata are constructori, iar clasel de baza ale ei nu au constructori definiti de programator;

Clasa derivat are constructori si cel putin o clasa de baza a ei are constructori.

Rezolvare

1. la instantierea obiectelor clasei derivate se aplica constructorii impliciti generati de compilator.

2. toti constructorii claselor de baza definiti de programator trebuie sa fie impliciti sau sa aiba toti parametrii impliciti.
La instantierea obiectelor clasei derivate nu se pot face initializari. La o astfel de instantiere se aplica constructorul
implicit al clasei derivate, generate de compilator si constructorii claselor de baza definiti de programator,

folosindu-se valorile implicite ale parametrilor acestora.

3. constructorul clasei derivate realizeaza toate initializarile atat pentru datele membru specifice clasei derivate cat si
pentru datele membru ale claselor de baza. Este necesar ca clasa derivata sa aiba acces la toate datele membru ale
claselor de baza care comporta initializari.

4. constructorul clasei derivate contine parametri pentru toate datele membru care comporta initializari. Valorile
parametrilor corespunzatori datelor membru specifice clasei derivate se utilizeaza pentru a initializa datele respective
in mod obisnuit (de obicei, in corpul constructorului clasei derivate se initializeaza aceste date, iar celelalte valori se
utilizeaza la apelul explicit al constructorului claselor de baza.

Exemple:

1. Clasa A este o clasa derivata a clasei de baza B. Nici una din clase nu are constructor.

La instantierea A a; obiectul a se instantiaza fara a se face initializari. La instantiere se aplica constructorii impliciti ai claselor generati de compilator.

2. Clasa A este o clasa derivata a clasei de abza B. Clasa B are constructor cu toti parametrii impliciti:

class B

};

Clasa A nu are constructor:

class A : public B

;

La declaratia A a;, obiectul a se initializeaza apeland constructorul cu parametrii impliciti al clasei B. Datele membre x si y se initializeaza cu 0, iar data membru z nu este initializata.

3. Clasa A este o clase derivata a clasei B. Clasa B nu are constructor definit de utilizator.

class B

;

Clasa A are un constructor.

class A : public B

...

};

Putem avea instantierile:

A a; -> inseamna a.x=0, a.y=0, a.u=1, a.v=2, a.z=1

A b(3,4); -> inseamna b.x=3, b.y=4, a.u=1, b.v=2, b.z=0

4. Clasa A este o clasa derivata a clasei B si ambele clase au constructorii definiti de programator.

class B

...

};

class A: public B

};

Utilizand aceleasi instantieri (declaratii) ca la punctul 3) obtinem aceleasi initializari (definiri).

Datele protected u si v ale clasei A se pot initializa chiar in antetul constructorului:

A (double p=0, double q=0, int x1=1, int y1=2) : B(p,q), u(x1), v(y1)

O clasa derivata este necesar sa aiba cel putin un constructor in cazul in care cel putin o clasa de baza are un constructor care nu este implicit sau nu are toti parametrii definiti.

Conversii

In principui, conversiile dintr-un tip predefinit intr-un tip abstract se realizeaza cu ajutorul constructorilor, iar conversia inversa cu ajutorul supraincarcarii operatorului cast. Conversia dintr-un tip abstract intr-un alt tip abstract se poate realiza prin ambele metode.

Fie A o clasa derivata a clasei de baza B. Un obiect care este o instantiere a clasei A se converteste implicit intr-un obiect al clasei B (deoarece obiectele clasei A sunt obiecte specializate ale clasei B).

Conversia inversa nu se poate realiza fara a fi definita printr-un constructor sau prin supradefinirea operatorului cast.

Conceptul de mostenire impune regului de conversie si pentru pointeri si referinte la obiecte ale claselor derivate se de baza.

Fie declaratiile:

A *pa,a;

B *pb,b;

Compilatorul de C++ permite atribuiri de tipul:

pa=&a;

pb=&b;

Acestea sunt realizate pe baza supraincarcarii implicite a operatorului adresa (& unar), care permite ca acesta se se aplice la o data sau obiect de orice tip.

O atribuire de forma: pb=&a; este acceptata de compilator. La fel si atribuirile: b=a; si pb=pa;.

In general, un pointer sau o referinta la un obiect al unei clase derivate se poate atribui la un pointer sau o referinta la un obiect al unei clase de baza a clasei derivate respective (conversii implicite corespunzatoare).

In schimb, atribuirile:

pa=&b;

pa=pb;

sunt eronate. Ele pot fi acceptate numai daca se utilizeaza expresii cast care realizeaza conversii explicite de pointeri.

pa=(A *)&b;

pa=(A *)pb;

Aplicatie a acestor conversii cast: crearea si prelucrarea de obiecte de tip colectie, colectie fiind un set de obiecte de diferite tipuri abstracte derivate dintr-un tip abstract de baza.

Redefinirea datelor membru ale unei clase de baza intr-o clasa derivata

O data membru a unei clase de baza se poate redefini ca data membru a unei clase derivate.

Exemplu:

class B

...

};

class A: public B

void afisare() const;

...

};

Functia membru afisare() se apeleaza pentru obiecte ale clasei derivate. Ea afiseaza datele membru ale clasei derivate, precum si datele membru mostenite de la clasa de baza B. Pentru a face distinctie intre datele membru ale clasei derivate si cele ale clasei de baza, se foloseste operatorul rezolutiei de domeniu astfel:

B::x -este data membru a clasei B

A::x -este data membru a clasei A

Putem defini functia afisare() astfel:

void A::afisare() const

Supraincarcarea functiile membru ale unei clase de baza intr-o clasa derivata

Functiile membru ale claselor de baza sunt mostenite de clasele derivate.

Astfel, daca B este o clasa de baza pentru clasa A si f este o functie membru a clasei B, atunci f este si o functie membru a clasei A.

Exemplu:

Putem avea declaratiile:

A a;

B b;

Apelurile b.f(...) si a.f(...) sunt corecte.

In mod analog, functiile friend ale clasei de baza sunt functii friend si pentru clasa derivata.

Mostenirea functiilor membru si friend are ca efect faptul ca supraincarcarea operatorilor pentru clasa de baza este valabila si pentru o clasa derivata a clasei de baza respective.

Cu toate acestea, nu totdeauna o functie mostenita de la o clasa de baza corespunde pentru a fi apelata cu obiecte ale unei clase derivate.

In astfel de situatii, functia respectiva se va supraincarca pentru clasa derivata. Functiile supraincarcate in acest fel se selecteaza nu numai dupa numarul si tipul parametrilor, ci si dupa obiectul pentru care sunt apelate.

Continuand exemplul de mai sus, al apelul:

b.f(...)

se apeleaza functia membru f a clasei de baza B, iar la apelul:

a.f(...)

se apeleaza functia membru f a clasei A daca f este supraincarcata pentru clasa A si functia membru f a clasei de baza B daca f nu este supraincarcata pentru clasa A.

O problema: in corpul functiei membru f, supraincarcata pentru clasa A, se doreste apelul functiei membru f a clasei B:

A::f(...)

Daca in corpul functiei f utilizam un apel obisnuit:

f(...)

atunci acesta se realizeaza pentru obiectul curent si conduce la un apel recursiv al functiei f. Pentru a apela functia membru f a clasei de baza B pentru obiectul curent care este o instantiere a clasei derivate A, folosim operatorul de rezolutie:

B::f(...)

Deci, corpul functiei f se va defini astfel:

A::f(...)

Exemplu:

class B

void afisare() const

...

};

class A: public B

void afisare() const();

...

};

Functia afisare() aplicata la o instantiere a clasei B va afisa valorile lui x si y.

void A::afisare() const

Functia afisare() aplicata la o instantiere a clasei A va afisa atat valorile lui x si y, si respectiv xx si yy.

Exemple de apeluri ale functiei afisare():

B b(1,2); //b.x=1, b.y=2;

A a(1,2,3,4); //a.x=3, a.y=4, a.xx=1, a.yy=2

b.afisare(); //se apeleaza functia membru afisare() a clasei B

Se va afisa: bx=1, by=2

La apelul functiei membru afisare() a clasei B

a.afisare();

se ca afisa: bx=3, by=4

dx=1, dy=2

Clase virtuale

Clasele obisnuite devin virtuale in procesul de derivare. Aceasta transformare se realizeaza pentru clasele de baza care intervin intr-o derivare. In acest scop, numele clasei de baza, utilizat la definirea unei clase derivate va fi precedat de cuvantul cheie virtual.

Exemplu:

class B1: public B

;

class B2: public B

;

class A: public B1, public B2

;

Clasa A mosteneste datele membru ale lui B de doua ori. Pentru a le mosteni o singura data, transformam clasa de baza B in clasa virtuala:

class B1: virtual public B

;

class B2: virtual public B

;

In acest caz, clasa A mosteneste datele membru ale lui B o singura data.

Acestea se mostenesc prin intermediul clasei de baza care este scrisa prima in lista claselor de baza. Astfel, datele membru ale clasei de baza virtuale B sunt mostenite prin intermediul clasei de baza B1.

Exemplu:

Daca definim clasa A inversand ordinea claselor de baza

classs A: public B2, public B1

;

atunci clasa A mosteneste datele membru ale clasei de baza virtuale B prin intermediul clasei de baza B2.

In clasele virtuale exista unele particularitati in legatura cu ordinea de apel a constructorilor la instantierea obiectelor clasei derivate.

Ca regula generala, intai se apeleaza constructorii claselor virtuale in ordinea in care au fost scrise clasele in lista claselor de baza ale clasei derivate. Dupa apelul constructorilor claselor virtuale, se apeleaza constructorii claselor de baza virtuale in ordinea in care clasele respective apar in lista claselor de baza a clasei derivate.

Intr-o ierarhie de clase, o clasa poate sa apara ca si clasa de baza de mai multe ori. Daca o clasa de baza apare intr-o ierarhie de mai multe ori, atat ca virtuala, cat si ca nevirtuala, atunci la instantierea unui obiect al clasei derivate se va apela constructorul clasei de baza o singura data pentru toate aparitiile virtuale ale clasei si de atatea ori cate aparitii

nevirtuale are clasa de baza respectiva.

Functii virtuale

O functie virtuala este o functie membru nestatica a carei prototip sau antet este precedat de cuvantul rezervat virtual. O astfel de functie se defineste ca fiind virtuala in clasa de baza si apoi ea poate fi definita in clasele derivate pentru care este nevoie sa se realizeze legatura dinamica.

In clasele derivate nu este nevoie sa se mai utilizeze cuvantul cheie virtual in prototipul sau antetul functiile virtuale.

Exemplu:

Declaram in clasa B functia:

virtual ... f(...);

Atunci functia f definita cu aceeasi lista de parametri este in mod automat virtuala pentru orice clasa A derivata direct sau indirect din B.

Functia f apare va o functie care are mai multe versiuni, una corespunde clasei de baza, iar celelalte claselor derivate.

Functiile virtuale din clasele derivate se suprapun (override) peste functiile virtuale din clasele de baza. Astfel, daca B este o clasa de baza si f o functie membru virtuala a acesteia iar A este o clasa derivat din B pentru care f este redefinita ca functie membru cu aceeasi lista de parametri, atunci vom spune ca o functie a lui A se suprapune peste functia membru f a lui B.

Selectarea versiunii care sa se apeleze se face pe baza unor tabele suplimentare construite in program in mod automat pentru functiile virtuale.

De asemenea, obiectele claselor care contin functii virtuale contin adresele tabelelor care le corespund. De aceea utilizarea functiilor virtuale necesita atat memorie suplimentara cat si un anumit timp suplimentar la executia

programului ambele fiind necesare pentru selectarea vresiunilor functiilor virtuale.

Rezulta ca functiile virtuale, desi sunt avatanjoase in multe cazuri, este bine sa nu se faca exagerari in utlizarea lor deoarece conduc la un consum sporit de memorie si timp de executie.

Reguli referitoare la functiile virtuale:

Functiile virtuale sunt functii membru nestatice ( care nu sunt statice )

Constructorii nu pot fi functii virtuale

Destructorii pot fi functii virtuale

Functiile inline nu pot fi functii virtuale

Observatii:

1. O functie membru este statica daca se declara cu o clasa de memorie static. Acestea se pot apela fie prin operatorul . sau ->, fie independent de un obiect al clasei pentru care este functie membru folosind operatorul

rezolutiei de domeniu:

nume_clasa::nume_functie_membru_statica

2. Functiile inline se declara folosind cuvantul rezervat inline. Aceasta are in general cel mult 3 intructiuni care se inlocuiesc inca de la compilare cu corpul functiei lor (echivalent cu #define din C).

Exemplu:

// Fisierul "curs6-2.h"

// Acesta este fisierul de interfata care contine declaratia

// clasei de baza "figura" si a subclaselor "dreptunghi" si

// "cerc". Programul se refera la calculul polimorfic al ariilor

// de figuri

#ifndef CURS6-2_H

#define CURS6-2_H

//#include <iostream.h>

//#include <string.h>

//#include <math.h>

#define PI 3.14159

class figura

;

class dreptunghi: public figura

;

class cerc: public figura

;

#endif // de la CURS6-2_HH

// Fisierul "curs6-2.cpp"

// Acesta este fisierul de implementare in care definim functiile

// membre ale clasei de baza "figura" si a subclaselor "dreptunghi"

// si "cerc".

#include "curs6-2.h"

#include <iostream.h>

#include <string.h>

#include <math.h>

// definim intai functiile membre ale clasei de baza "figura"

figura :: figura(char *nume, int i, int j)

double figura :: aria()

void figura :: print()

// in continuare, vom defini functiile membre ale clasei derivate

// "dreptunghi" din clasa de baza "figura"

dreptunghi :: dreptunghi(char *nume, int i1, int j1, int i2, int j2):

figura(nume, i1, j1)

double dreptunghi :: aria()

void dreptunghi :: print()

// in continuare, vom defini functiile membre ale clasei derivate

// "cerc" din clasa de baza "figura"

cerc :: cerc(char *nume, int i1, int j1, int r):

figura(nume, i1, j1)

double cerc :: aria()

void cerc :: print()

// Fisierul "main6-2.cpp"

// Acesta este fisierul de aplicatie pentru clasa figura, si

// subclasele dreptunghi si cerc.

#include "curs6-2.h"

#include <iostream.h>

void main()

Exercitii propuse spre implementare:

1. Scrieti un program C++ care implementeaza o structura de lista, clasele derivate de stiva si coada. Implementarea se va face in fisiere separate si va contine operatiile principale de push, pop, stergerea, inserarea, afisarea

structurilor precedente

Folositi aceste biblioteci la problemele cunoscute:

sortare prin interclasare (lista);

parcurgerea grafurilor (in latime-coada, in adancime-stiva);

evaluarea expresiilor aritmetice (stive);

2. Implementati o ierarhie de clase pentru urmatoarele patrulatere: paralelogram, dreptunghi, patrat si romb. Obiectele acestor clase sunt patrulatere care au doua laturi paralele cu axa Ox. Clasele cu functii membru care permit realizarea urmatoarelor operatii asupra obiectelor:

afisare pe ecranul grafic;

stergere de pe ecranul grafic;

deplasare pe ecran grafic;

calculul perimetrului si ariei figurii geometrice;

3. Folosind clasa Lista (de la ex. 1) definiti o clasa Polinom care sa simuleze polinomul: an*xn+an-1*xn-1+...+a0, ai R. Definiti operatiile +,-,* si evaluarea unui polinom intr-un punct. (citirea si afisarea lor). De exemplu: 3*x3+0*x2+0*x+2 va fi afisat 3x^3+2.



Document Info


Accesari: 4444
Apreciat: hand-up

Comenteaza documentul:

Nu esti inregistrat
Trebuie sa fii utilizator inregistrat pentru a putea comenta


Creaza cont nou

A fost util?

Daca documentul a fost util si crezi ca merita
sa adaugi un link catre el la tine in site


in pagina web a site-ului tau.




eCoduri.com - coduri postale, contabile, CAEN sau bancare

Politica de confidentialitate | Termenii si conditii de utilizare




Copyright © Contact (SCRIGROUP Int. 2024 )