8.3. No?iune de func?ie virtual?

8.3. No?iune de func?ie virtual?

O func?ie membr? devine func?ie virtual?, dac? vom scrie cuv?ntul-cheie virtual ?n antetul func?iei la declararea ei ?n clas?. C?nd o func?ie virtual? se aplic? direct la o instan?? a clasei, apelul acestei func?ii nu se deosebe?te de apelul oric?rei altei func?ii care nu este virtual?. Este clar la ce obiect se aplic?, ?i anume: reprezentantul c?rei clase este acest obiect.

Un a?a apel se rezolv? ?n timpul compil?rii ?i edit?rii de leg?turi, a?a c? la momentul rul?rii programului apelul acesta este deja rezolvat. Apeluri de acest tip se numesc leg?turi ini?iale, timpurii sau statice (engl. early binding).

Altfel stau lucrurile, dac? o func?ie virtual? se apeleaz? printr-un pointer sau o referin??. ?n acest caz poate s? nu fie clar la ce obiect indic? pointerul, sau ce obiect este trimis prin referin?? – cel de baz? sau unul dintre cele derivate (doar ?ti?i regulile de atribuire ?ntre clasele de baz? ?i cele derivate). ?n a?a condi?ii, tipul obiectului poate fi aflat numai ?n timpul rul?rii, c?nd programul a ajuns la instruc?iunea ?n care se con?ine acest apel al func?iei noastre.

Dac? func?ia noastr? este virtual?, rezolvarea apelului ei va fi am?nat? p?n? ?n momentul ?n care programul rulat nu ajunge la acest apel. Adic?, func?ia va fi dinamic apelat? ?n dependen?? de tipul obiectului spre care indic? pointerul, sau care a fost trimis prin referin??. Astfel de apeluri se numesc leg?turi ulterioare, t?rzii sau dinamice (engl. late binding).

Deocamdat? nu vom intra ?n detaliile de realizare a mecanismului leg?turilor ulterioare (el poate fi realizat, de exemplu, prin Tabelele Metodelor Virtuale, engl. VMTVirtual Methods Table). D?m forma general? de declarare a func?iilor virtuale ?i vom modifica exemplul nostru pentru a demonstra utilizarea acestor func?ii.

Func?ia virtual? este o func?ie membr? a clasei, care, fiind apelat? prin pointer sau prin referin?? la un obiect al acestei clase, se apeleaz? dinamic.

Adic?, apelul dat va fi rezolvat nu ?n timpul compil?rii ?i edit?rii de leg?turi, ci la momentul apelului func?iei pe parcursul rul?rii programului.

Forma general? de declarare a unei func?ii virtuale este urm?toarea:

virtual [tip ] [nume_clas?::]nume_func?ie

                                  ([list?_parametri])

{

   …

}

sau

tip virtual [nume_clas?::]nume_func?ie

                                  ([list?_parametri])

{

   …

}

Dac? o func?ie virtual? se suprascrie ?ntr-o clas? derivat?, la suprascriere cuv?ntul-cheie virtual poate fi omis.

Func?ia virtual? nu poate fi declarat? ca o func?ie generic?.

Func?ia declarat? ?ntr-o clas? ca func?ie membr? virtual?, fiind mo?tenit?, r?m?ne virtual? ?i ?n orice alt? clas? derivat? a acestei clase de baz?. ?n orice clas? derivat? func?ia virtual? poate fi, la necesitate, suprascris?.

Revenim la exemplul nostru. ?n primul r?nd, vom transforma func?ia print() ?n una virtual?. Pentru aceasta, ?n fi?ierul ”fr.cpp” scriem cuv?ntul-cheie virtual ?nainte de antetul func?iei print() ?n declararea clasei fractie_rationala, astfel ca antetul nou s? fie:

virtual ostream &print(ostream &stream)

F?r? a face alte modific?ri, trecem la fi?ierul ”rfs.cpp”. ?l compil?m ?i lans?m programul. Efectul ultimei modific?ri se observ? imediat:

Sunt in new pentru fr!
Sunt in new pentru fr!
Sunt in new pentru fr!
Sunt in new pentru fr!
Sunt in new pentru fr!
Sunt in new pentru fr!

Vectorul vrp initial:
2/3
1,3/5
-2,1/3
1,1/4
4/5
-1,2/3

Vectorul vrp dupa sortarea:
1,1/4
-2,1/3
1,3/5
2/3
-1,2/3
4/5
Sunt in delete pentru fr!
Sunt in delete pentru fr!
Sunt in delete pentru fr!
Sunt in delete pentru fr!
Sunt in delete pentru fr!
Sunt in delete pentru fr!

Observ?m c? obiectele s-au afi?at, ?i anume acelea care au fost create ?i alocate dinamic. Totu?i, sortarea nu s-a f?cut corect. Este clar de ce. Din cauza compar?rii, mai bine spus, din cauza conversiei. Operatorul de conversie la double fiind non-virtual a fost folosit permanent din clasa de baz? fractie_rationala. S?-l modific?m ?n unul virtual. Schimb?m antetul lui ?n fi?ierul ”fr.cpp” ?n unul:

virtual operator double()

Iar??i compil?m ?i lans?m programul ”rfs.cpp”, rezultatul multa?teptat va fi:

Sunt in new pentru fr!
Sunt in new pentru fr!
Sunt in new pentru fr!
Sunt in new pentru fr!
Sunt in new pentru fr!
Sunt in new pentru fr!

Vectorul vrp initial:
2/3
1,3/5
-2,1/3
1,1/4
4/5
-1,2/3

Vectorul vrp dupa sortarea:
-2,1/3
-1,2/3
2/3
4/5
1,1/4
1,3/5

Sunt in delete pentru fr!
Sunt in delete pentru fr!
Sunt in delete pentru fr!
Sunt in delete pentru fr!
Sunt in delete pentru fr!
Sunt in delete pentru fr!

Acum ?i afi??rile, ?i sortarea sunt corecte. Atrage?i aten?ia c? fragmentul precedent din func?ia main(), care utilizeaz? clasa vector, afi?eaz? acela?i rezultat pe care el l-a afi?at ?i f?r? func?ii virtuale.

Exerci?iul 8.1. Suprascrie?i operatorii new ?i delete ?n clasele rational_fara_semn ?i rational. ?n versiunile suprascrise ale acestor operatori modifica?i numai con?inutul mesajelor despre func?ia respectiv?. Rezultatul afi?at de fragmentul precedent dup? exerci?iul dat trebuie s? fie urm?torul:

Sunt in new pentru fr!
Sunt in new pentru rfs!
Sunt in new pentru r!
Sunt in new pentru rfs!
Sunt in new pentru fr!
Sunt in new pentru r!

Vectorul vrp initial:
2/3
1,3/5
-2,1/3
1,1/4
4/5
-1,2/3

Vectorul vrp dupa sortarea:
-2,1/3
-1,2/3
2/3
4/5
1,1/4
1,3/5

Sunt in delete pentru r!
Sunt in delete pentru r!
Sunt in delete pentru fr!
Sunt in delete pentru fr!
Sunt in delete pentru rfs!
Sunt in delete pentru rfs!

Analiza?i acest rezultat ?i face?i concluzii.

S? vedem cum lucreaz? func?iile virtuale cu referin?e. ?n primul r?nd, verific?m c? operatorul de inser?ie << este supra?nc?rcat pentru clasa fractie_rationala ?n fi?ierul ”fr.cpp” astfel:

ostream &operator << (ostream& stream, const fractie_rationala &fr)
{
return fr.print(stream);
}

Dac? este a?a, putem elimina din fi?ierul ”rfs.cpp” supra?nc?rc?rile acestui operator pentru clasele rational_fara_semn ?i rational. Dup? ce func?ia print() a devenit virtual?, nu mai avem nevoie de supra?nc?rcarea operatorului de inser?ie pentru clasele derivate (cu condi?ia c? func?ia virtual? print() va fi suprascris? ?n fiecare clas? derivat?).

Acum verifica?i c? operatorul de inser?ie <<, supra?nc?rcat pentru clasa fractie_rationala de pe atunci c?nd clasele derivate nici nu existau, ?n cazul:

cout << fractie_rationala(3, 5) << endl;
va afi?a:
3/5

?n cazul:
cout << rational_fara_semn(2, 1, 7) << endl;
va afi?a:
2,1/7

iar ?n cazul:
cout << rational(-1, 4, 5) << endl;
va afi?a:
-1,4/5

Este ceva fantastic, nu-i a?a?

Exemplele cercetate de utilizare a func?iilor virtuale au demonstrat esen?a polimorfismului ?n timpul rul?rii.

Descriem propriet??ile func?iilor virtuale:

      • O func?ie membr? poate s? devin? virtual? ?ncep?nd de la orice nivel de ierarhie, ?ns? dac? o func?ie membr? a devenit virtual? la un nivel de ierarhie ?ntr-o clas?, ea va r?m?ne virtual? ?n toate genera?iile viitoare ale acestei clase. Adic?, atributul de virtualitate se mo?tene?te ?mpreun? cu ?ns??i func?ia. De exemplu, ?n clasa rational_fara_semn este func?ia membr? nevirtual? la_fr(). Putem s-o suprascriem ?n clasa derivat? rational ca virtual? (prefix?nd antetul ei cu cuv?ntul-cheie virtual). Atunci, ?n clasa rational_fara_semn func?ia la_fr() r?m?ne nevirtual?, a?a cum a fost; ?n clasa rational func?ia suprascris? la_fr() va fi virtual?. Virtual? ea va fi ?i ?n orice alt? clas? derivat? de la rational, nu conteaz? va fi ea suprascris? sau nu ?n clasa derivat?.
      • Suprascriind o func?ie virtual? ?ntr-o clas? derivat?, se poate omite cuv?ntul-cheie virtual ?n antetul de suprascriere, dar poate fi ?i l?sat, pentru ca lucrurile s? fie mai clare.
      • Dac? vre?i s? suprascrie?i o func?ie virtual? ?ntr-o clas? derivat?, trebuie s? respecta?i antetul func?iei virtuale corespunz?toare din clasa de baz?. Antetul la suprascriere trebuie s? fie exact acela?i, cu excep?ia c? cuv?ntul virtual poate fi omis. De exemplu, func?ia de afi?are ?n clasele fractie_rationala, rational_fara_semn, rational are unul ?i acela?i antet virtual ostream &print(ostream &stream=cout).
      • Orice func?ie virtual? (?n afar? de destructor) a oric?rei clase poate fi supra?nc?rcat?, ?ns? atributul de virtualitate nu se transmite la supra?nc?rcare. Func?ia care supra?ncarc? o func?ie virtual? poate fi ?i ea virtual? (dac? cuv?ntul virtual va fi indicat explicit la supra?nc?rcare). ?ns?, independent de faptul va fi ea virtual? sau nu, func?ia care supra?nc?rc? o func?ie virtual? nu va avea nici o leg?tur? cu ultima. De exemplu, putem s? mai introducem ?n  clasa  rational_fara_semn  o  func?ie  cu  antetul virtual ostream &print(ostream &stream, char *mesaj), care afi?eaz? un mesaj ?nainte de a afi?a valoarea frac?iei. Aceasta func?ie nou? nu va avea nimic comun cu func?ia ini?ial? virtual ostream &print(ostream &stream=cout) ?i nici nu va influen?a asupra exemplelor precedente cu apelurile func?iilor virtuale prin pointeri sau referin?e.
      • Cuv?ntul-cheie virtual se scrie numai ?n antetul func?iei membre ?n declararea clasei. Dac? descrierea func?iei se face aparte, dup? declararea clasei, cuv?ntul virtual a doua oar? nu se mai scrie, exact a?a cum ?i atributul friend la func?ii prietene.
      • Constructorul nu poate fi virtual, pe c?nd destructorul poate s? fie virtual ?i, ?n unele cazuri, chiar trebuie s? fie virtual. G?ndi?i-v? la cazul obiectelor pointate de c?tre vectorul de pointeri vectorp ?i la apelurile destructorilor acestor obiecte ?n destructorul clasei vectorp.
Clasa care con?ine doar o func?ie virtual? se nume?te clas? polimorfic?

Cu toate c? utilizarea func?iilor virtuale aduce un aport esen?ial ?n realizarea principiilor de polimorfism, folosind func?iile virtuale trebuie s? con?tientiz?m faptul c? leg?turile ulterioare reduc viteza de executare a programului. Aceast? reducere poate fi neesen?ial?, dac? ?n program nu sunt multe apeluri ale func?iilor virtuale prin intermediul pointerilor sau referin?elor. Din contra, executarea programului poate fi cu mult mai lent?, dac? apelurile de acest tip se ?nt?lnesc ?n program destul de des, mai ales dac? ele se afl? ?n cicluri mari ?i ad?nci.
_________________________
Autorul: dr.conf. S. Pereteatcu

 

рассказать друзьям и получить подарок

Оставить комментарий

Ваш email не будет опубликован. Обязательные поля отмечены *

Вы можете использовать это HTMLтеги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Translate Переводчик

Подписка на новости

SmartResponder.ru
Ваш e-mail: *
Ваше имя: *

Хостинг для Wordpress сайтов