8.4. Func?ii virtuale pure ?i clase abstracte

8.4. Func?ii virtuale pure ?i clase abstracte

?n limbajul C++ a fost introdus? suplimentar o form? special? de func?ii membre virtuale, pentru care nu se indic? corpul func?iei. A?a func?ii virtuale au c?p?tat denumirea de func?ii virtuale pure.

Func?ia virtual? pur? este o func?ie virtual? special? de formavirtual [tip] [nume_clas?::]nume_func?ie ([list?_parametri]) = 0;

sau

tip virtual [nume_clas?::]nume_func?ie ([list?_parametri]) = 0;

Ea nu are corp ?i nu poate fi executat?. Func?ii virtuale pure se declar? ?n clase cu scopul de a fi suprascrise ?n clase derivate respective.

Sunt cel pu?in dou? motive de utilizare a func?iilor virtuale pure:

  • Clasa de baz? nu are suficiente informa?ii ?i nu este ?n stare s? descrie complet func?ionalitatea unei func?ii membre virtuale.
  • Pentru a impune suprascrierea for?at? de c?tre programatori a func?iilor virtuale pure ?n propriile sale clase.

Clasele ce con?in func?ii virtuale pure au c?p?tat denumirea de clase abstracte. De ce abstracte? Pentru c? compilatorul nu permite crearea exemplarelor de obiecte de clase care con?in func?ii virtuale pure. A?a clase sunt folosite numai ca baz? pentru crearea claselor derivate noi.

Clas? care con?ine sau mo?tene?te f?r? suprascriere m?car o func?ie virtual? pur? se nume?te clas? abstract?. Nu se admite crearea exemplarelor de obiecte ale clasei abstracte. Clasa abstract? serve?te numai ca baz? pentru declararea altor clase (derivate).

?ntr-o clas? abstract? putem formula principiile de func?ionare a unei ierarhii de clase, dup? care ?n clasele derivate vom concretiza func?ionalitatea prin suprascrierea func?iilor virtuale pure.

De?i exemplarele de obiecte ale claselor abstracte nu pot fi create, declar?rile pointerilor cu tipul de baz? a unei clase abstracte ?i declar?rile parametrilor-referin?elor la o clas? abstract? sunt perfect valide ?i utilizate la descrierea principiilor de func?ionare a unei ierarhii de clase.

Clasa derivat? nu este obligatorie s? suprascrie toate func?iile virtuale pure mo?tenite de la o clas? abstract?, ?i dac? ea nu face acest lucru complet, ea continu? s? r?m?n? abstract?. Mai mult ca at?t, o clas? derivat? abstract? poate s? defineasc? suplimentar func?ii virtuale pure proprii. ?i numai ?n cazul c?nd ?ntr-o clas? derivat? sunt suprascrise toate func?iile virtuale pure, ?i nu s-au mai ad?ugat func?ii virtuale pure noi, clasa derivat? devine o clas? concret?, nu abstract?, ?i apare posibilitatea de creare a instan?elor acestei clase.

O clas? derivat? care mo?tene?te de la o clas? non-abstract? va fi declarat? ca fiind o clas? abstract?, dac? ?n ea va fi introdus? m?car o func?ie virtual? pur? proprie, sau/?i suprascris? ca pur? m?car o func?ie virtual? mo?tenit?. Aceste reguli r?m?n ?n vigoare ?i ?n cazul mo?tenirii multiple.

Revenim la exemplul nostru. ?n calitate de clas? abstract? putem utiliza no?iunea de un num?r abstract care poate fi convertit la un zecimal ?n virgul? mobil? ?i poate fi afi?at. Inser?m ?n fi?ierul ”fr.cpp”, ?nainte de declararea clasei fractie_rationala, urm?torul fragment care declar? clasa abstract? numar:

class numar
{
   public:
      virtual ostream  &print(ostream &stream) = 0;
      virtual operator double() = 0;
};

Totodat?, modific?m antetul de declarare a clasei fractie_rationala ca el s? aib? urm?toarea redactare:

class fractie_rationala: public numar

Acum, ?n exemplul nostru ierarhia claselor va fi compus? din patru clase. La baza acestei ierarhii st? clasa abstract? numar cu dou? func?ii virtuale pure. Clasa fractie_rationala va fi acum o clas? derivat? de la numar. Clasa fractie_rationala nu va fi abstract?, fiindc? ?n ea sunt suprascrise toate func?iile virtuale pure mo?tenite de la clasa de baz? numar.

Mai departe, f?r? modific?ri, clasa rational_fara_semn mo?tene?te de la fractie_rationala ?i clasa rational se bazeaz? pe clasa rational_fara_semn. Dac? acum lans?m programul cu func?ia main() din fi?ierul ”rfs.cpp”, toate rezultatele vor fi exact acelea?i care au fost ?i ?nainte de ultima modificare.

?n caz de necesitate, func?ia virtual? pur? poate fi definit? de c?tre utilizatorul clasei abstracte ?n orice loc al programului unde poate fi definit? orice func?ie. Forma de definire este urm?toarea:

tip nume_clas? :: nume_func?ie (list?_parametri) {}

Definirea func?iilor virtuale pure nu modific? clasa din abstract? ?n non-abstract?, adic? nu exclude necesitatea de suprascriere a func?iilor virtuale pure ?n clasele derivate pe care dorim s? le prefacem ?n neabstracte.

Pentru exemplu, inser?m ?n fi?ierul ”rfs.cpp” (pentru claritate – ?nainte de declararea clasei rational) urm?toarea defini?ie a func?iei virtuale pure print membr? a clasei numar:

ostream &numar::print(ostream &stream)
{
   return stream << "Q:";
}

Dup? cum observ?m, func?ia transmite ?n streamul de ie?ire stream textul "Q:" ?i ?ntoarce ca rezultat referin?a la acest stream. Modific?m suprascrierea func?iei print() ?n clasa rational, inser?nd apelul func?iei print() a clasei numar:

ostream &print(ostream &stream=cout)
      {
         numar::print(stream); // acest apel a fost inserat
         if(semn=='-')
            stream << '-';
         if (part_int)
            stream << part_int << ",";
         return fractie_rationala::print(stream);
      }

Lans?m programul ?i observ?m efectul. Afi?area oric?rui obiect al clasei rational va fi prefixat? de textul "Q:"(de exemplu, Q:-1,4/5).

Ca ?i orice alt? func?ie virtual?, destructorul clasei abstracte poate fi pur. ?n acest caz el neap?rat trebuie s? fie definit undeva ?n program, fiindc?, dup? cum ?ti?i, destructorul clasei derivate (fie el implicit, fie el definit explicit ?n clasa derivat?) apeleaz? automat destructorul clasei de baz?, ceea ce cere ca destructorul clasei de baz? s? fie definit. Dac? nu defini?i destructorul virtual pur al clasei de baz? abstracte, editorul de leg?turi va da un mesaj de eroare c? nu poate g?si func?ia-destructor a clasei de baz?.

Cu toate c? programul va fi compilat cu succes, editorul de leg?turi nu va crea modulul executabil ?i nu ve?i putea lansa programul ?n execu?ie.

Exerci?iul 8.2. G?ndi?i-v? la cazul c?nd avem un lan? de clase abstracte derivate una de la alta, fiecare cu propriul destructor virtual pur, ?i ultima clas? derivat? nu este abstract?.

?n continuarea exemplului nostru inser?m ?n sec?iunea public a clasei numar (fi?ierul ”fr.cpp”) urm?torul cod:

virtual ~numar() = 0; // destructorul virtual ?i pur

Compilarea programului din fi?ierul ”rfs.cpp” va trece cu succes. ?ns?, dac? vom ?ncerca s? construim modulul executabil, vom primi de la editorul de leg?turi un mesaj de tipul ”Error: Unresolved external 'numar::~numar()'”. Este suficient de a insera defini?ia destructorului:

numar::~numar()
{
 
}

?n orice loc ?n fi?ierul ”fr.cpp” sau ”rfs.cpp”, unde, ?n principiu, poate fi scris? o func?ie (pentru exemplu, dup? func?ia main(), cu toate c? solu?ia aceasta nu este reu?it?), ?i func?ionalitatea programului va fi restabilit?, iar rezultatele vor fi acelea?i ca ?i data trecut?.

Nu v? descuraja?i de faptul c? acest destructor este vid, la necesitate pute?i insera ?n el orice instruc?iuni.
_________________________
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 сайтов