CPP_RO_7_MOSTENIRE

7.1. Clase derivate

7.1. Clase derivate

A venit timpul s? cre?m pe baza clasei fractie_rationala o clas? derivat? care va mo?teni de la cea de baz? toate propriet??ile ?i va avea ?n plus ni?te propriet??i ?i posibilit??i noi. ?n mod normal, vom ad?uga la frac?ia pozitiv? ra?ional? simpl? partea ?ntreag?. Posibil, a?a se face ?i la ?coal? c?nd se studiaz? cursul de algebr? elementar?. Aceast? clas? derivat? vom numi-o rational_fara_semn.

Lu?m ultima versiune a programului cu clasa fractie_rationala; ?tergem din ea func?ia main() ?i scriem restul (includeri, defini?ia clasei, defini?iile func?iilor membre ?i a celor prietene) ?n fi?ierul cu numele ”fr.cpp”. Acest fi?ier va fi inclus permanent ?n exemplele care vor urma.

Cre?m un fi?ier nou cu numele ”rfs.cpp” ?n care vom scrie urm?torul cod:

// includem fi?ierul cu defini?ia clasei de baz?
#include "fr.cpp" 
// Clasa derivat? include numere ra?ionale f?r? semn (pozitive)
class rational_fara_semn: public fractie_rationala
{
   protected:
      unsigned int part_int;
};
 
void main()
{
   rational_fara_semn a, b;
   cout << "a=" << a << endl;
   cout << "b=" << b << endl;    char ch; cin >> ch;
}

Fiind lansat, acest program va afi?a urm?toarea ie?ire:

a=0/1
b=0/1

?ncerc?m s? ne descurc?m ?n ceea ce a fost f?cut.

?n primul r?nd, am creat o clas? nou? cu denumirea rational_fara_semn, ?ns? ea nu a fost creat? pe loc gol, ci pe baza clasei fractie_rationala (anume construc?ia ": public fractie_rationala" indic? la aceasta). Ceea ce ?nseamn? c? clasa rational_fara_semn mo?tene?te de la clasa fractie_rationala. Clasa de la care se mo?tene?te se nume?te clas? de baz?, sau clas? p?rinteasc?, sau superclas?. Clasa care mo?tene?te se nume?te clas? derivat?, sau clas? mo?tenitoare, sau subclas?.

Fiecare obiect al clasei derivate rational_fara_semn va con?ine c?mpurile numarat ?i numit, mo?tenite de la clasa de baz?, ?i c?mpul part_int, care a fost ad?ugat ?n sec?iunea protected la definirea clasei rational_fara_semn. ?n acest c?mp se va p?stra partea ?ntreag? a num?rului ra?ional. C?mpurile numarat ?i numit r?m?n protejate ?i ?n clasa derivat?, precum au ?i fost ?n clasa de baz?. Despre aceasta ne spune cuv?ntul public situat ?nainte de numele clasei de baz?  la definirea  clasei  derivate  (acest  mecanism  va  fi  explicat ?n 7.3).

?n clasa derivat? rational_fara_semn nu a fost ad?ugat? nici o func?ie membr?. Aceasta ?nseamn? c? ea va mo?teni f?r? suprascriere toate func?iile membre ale clasei de baz? (?ns? nu constructorii ?i destructorul care nu se mo?tenesc, ?i func?ia eroare() care nu va fi accesibil? ?n clasa derivat?, deoarece ea este privat? ?n clasa de baz?).

?n afar? de aceasta, ?n clasa derivat? vor fi definite implicit un constructor f?r? parametri rational_fara_semn() ?i un destructor ~rational_fara_semn(). ?n exemplul nostru acest constructor va fi apelat automat ?n func?ia main() o dat? la crearea obiectului a ?i a doua oar? – la crearea obiectului b. Destructorul va fi apelat pentru fiecare din aceste obiecte la ie?ire din func?ia main().

Constructorul implicit nu face nimic ?n afar? de apelarea constructorului clasei de baz?. Acest lucru este asigurat de c?tre compilator. Constructorul clasei de baz? va fi apelat f?r? argumente (adic?, ?n cazul nostru cu valorile implicite pentru ambele argumente). Ca rezultat, c?mpurile numarat ?i numit vor fi puse respectiv pe 0 ?i 1 la ambele obiecte (a ?i b). C?mpul part_int va r?m?ne neini?ializat la ambele obiecte, fiindc? ?n constructorul implicit al clasei derivate nu este prev?zut? nici o ini?ializare.

Dac? clasa de baz? fractie_rationala nu ar avea nici constructorul f?r? parametri, nici constructorul cu valorile implicite pentru to?i parametrii, programul din acest exemplu nu ar fi compilat. Pur ?i simplu, compilatorul nu ar avea posibilitatea de a apela constructorul de baz? din constructorul implicit al clasei derivate.

Destructorul implicit al clasei derivate la fel nu face nimic, ?n afara apelului automat al destructorului clasei de baz?. Lucru care va fi asigurat de c?tre compilator. Ne vom aduce aminte c? destructorul clasei de baz? fractie_rationala nu face nimic.

Dup? cum s-a spus, ?n clasa rational_fara_semn nu a fost suprascris? nici o func?ie membr? mo?tenit? de la clasa fractie_rationala. Aceasta ?nseamn? c?, lucr?nd cu obiectele clasei derivate, fiecare din aceste func?ii vor afecta numai c?mpurile mo?tenite, precum ele ar lucra cu obiectele clasei de baz?. ?i toate celelalte func?ii, fie c? sunt sau nu prietene ale clasei de baz?, primind obiectele derivate vor lucra cu ele precum cu obiectele de baz?. De aceea, ?n instruc?iunile:

cout << "a=" << a << endl;
cout << "b=" << b << endl;

pentru a transmite obiectele a ?i b ?n streamul de ie?ire, va fi apelat? func?ia care supra?ncarc? operatorul << pentru clasa de baz?. Fiind aplicat? la obiectele clasei rational_fara_semn, aceast? func?ie va afi?a doar num?r?torul ?i numitorul, neav?nd idee c? obiectele clasei derivate mai au ?nc? un c?mp. Acum este clar? ie?irea afi?at? de c?tre programul exemplului nostru.

De asemenea a devenit clar c? multe func?ii ale clasei de baz? trebuie s? fie suprascrise ?n clasa derivat?. Va trebui de supra?nc?rcat ?i func?iile prietene ale clasei de baz?. Dar ?n primul r?nd trebuie definit constructorul clasei derivate. Adic?, clasa rational_fara_semn cere o dezvoltare esen?ial?, ?ns? lucrul acesta va fi f?cut, pe pa?i, ?n sec?iunile urm?toare.

?n baza clasei rational_fara_semn putem crea alte clase derivate, iar ?n baza lor – alte, ?i a?a mai departe. Procesul nu este limitat ?i poate fi ?ntins ?n timp, ?mpr??tiat prin diferite fi?iere, executat de diferi?i programatori etc.

Pentru exemplu, cre?m ?n baza clasei rational_fara_semn clasa derivat? rational, la care vom ad?uga semnul num?rului ra?ional, permi??nd, astfel, posibilitatea de a lucra ?i cu numere ra?ionale negative. Inser?m ?n programul exemplului nostru, dup? declararea clasei rational_fara_semn, urm?torul cod:

// Clasa derivat? – numere ra?ionale
class rational: public rational_fara_semn
{
   private:
      char semn;
};

cu declararea de machet? a clasei derivate rational, care mo?tene?te nemijlocit de la clasa rational_fara_semn. Dup? cum observ?m, s-a mai ad?ugat un c?mp nou, cu numele semn, destinat pentru p?strarea semnului num?rului ra?ional. Am inclus acest c?mp anume ?n sec?iunea private special pentru exemplu, ca s? avem o clas? cu c?mpul privat.

Din celelalte considerente, locul c?mpului dat era mai potrivit ?n sec?iunea protected. Pentru clasa rational clasa fractie_rationala va fi un str?mo?. Clasa rational va fi dezvoltat? ?n cele ce urmeaz?, ?n paralel cu clasa rational_fara_semn.
_________________________
Autorul: dr.conf. S. Pereteatcu

7.2. Controlul accesului la mo?tenire

7.2. Controlul accesului la mo?tenire

Clasa derivat?, care mo?tene?te de la o singur? clas? de baz? (se admite mo?tenirea ?i de la mai multe clase, a?a-numita mo?tenire multipl?), este definit? dup? urm?toarea form? general?:

 

class nume_clas?_derivat?: [mod_mo?tenire] nume_clas?_de_baz?{   [date_func?ii_prototipuri_prieteni]

   [specificator_de_acces:

      date_func?ii_prototipuri_prieteni

    specificator_de_acces:

      date_func?ii_prototipuri_prieteni

    …

    specificator_de_acces:

      date_func?ii_prototipuri_prieteni]

} [list?_de_obiecte];

[defini?ia_func?iei_membre_sau_prietene

defini?ia_func?iei_membre_sau_prietene

defini?ia_func?iei_membre_sau_prietene]

Aceasta era forma general? pentru cazul c?nd clasa derivat? mo?tene?te de la o singur? clas? de baz? (engl. single inheritance). ?n C++ este posibil? ?i mo?tenirea multipl?, ea va fi descris? aparte.

De la o clas? de baz? putem crea mai multe clase derivate, fiecare dintre care corespunz?nd cerin?elor aplica?iei. Astfel, ob?inem o extindere ?n l??ime. Orice clas? derivat? poate servi ca  o clas? de baz? pentru o alt? clas? derivat?, ?i a?a mai departe. Ca rezultat avem o extindere a ierarhiei. Astfel, se creeaz? genera?ii de clase de baz? ?i de clase derivate. Procesele cu a?a extinderi nu sunt limitate.

?n memoria alocat? pentru un obiect derivat, mai ?nt?i vor fi alocate c?mpurile mo?tenite de la clasa de baz? ?i apoi c?mpurile declarate ?n defini?ia clasei derivate, ?n ordinea apari?iei lor.

Specificatorul mod_mo?tenire indic? ce tip de acces vor avea membrii mo?teni?i ?n clasa derivat?. Tipul lor de acces ?n clasa derivat? depinde de tipul lor de acces ?n clasa de baz? ?i de modul de mo?tenire, conform Tabelului 7.1. Dac? specificatorul mod_mo?tenire va fi omis, atunci, implicit, pentru clase va fi considerat modul de mo?tenire private, pe c?nd pentru structuri (?n loc de class se folose?te cuv?ntul-cheie struct), implicit, va fi considerat modul de mo?tenire public.

Din acest Tabel observ?m c?, independent de modul de mo?tenire, membrii priva?i ai clasei de baz? nu vor fi accesibili direct ?n clasa derivat?.

Tabelul 7.1. Tipul de acces ?n clasa derivat? la un membru mo?tenit

Modul de mo?tenire ?

public protected private
Tipul de acces la un membru ?n clasa de baz? ?
public public protected private
protected protected protected private
private Nu este accesibil Nu este accesibil Nu este accesibil

Modul de acces public nu schimb? tipul de acces la membrii publici ?i proteja?i. Modul de mo?tenire protected modific? to?i membrii publici, mo?teni?i de la clasa de baz?, ?n membri proteja?i din clasa derivat?. Dac? modul de mo?tenire este private, atunci to?i membrii – ?i publici ?i proteja?i, mo?teni?i de la clas? de baz?, vor fi modifica?i ?n membri priva?i din clasa derivat?.
_________________________
Autorul: dr.conf. S. Pereteatcu

 

7.3. Definirea ?i supra?nc?rcarea constructorului ?n clasa derivat?

7.3. Definirea ?i supra?nc?rcarea constructorului ?n clasa derivat?

Definind ?i supra?nc?rc?nd constructorii ?n clasa derivat?, trebuie de asigurat ?n ei apelul constructorului clasei de baz?. Dac? constructorul clasei de baz? nu va fi apelat explicit, compilatorul ?l va apela implicit ?i f?r? argumente; ?i ?n cazul c?nd clasa de baz? nu dispune de un a?a constructor, compilatorul va semnala o eroare.

Forma general? de definire ?i supra?nc?rcare a constructorului clasei derivate arat? astfel:

[nume_clas?_derivat?::] nume_clas?_derivat?(list?_parametri)
[: nume_clas?_baz?(list?_argumente)]
{

// codul constructorului clasei derivate  

}

Aceasta a fost forma general? pentru cazul c?nd clasa derivat? mo?tene?te de la o singur? clas? de baz?. Cazul de mo?tenire multipl? va fi descris aparte. Memoriza?i c? mai ?nt?i se apeleaz? constructorul clasei de baz?, apoi se execut? ?nsu?i constructorul clasei derivate. Pentru destructor ordinea este invers?. Adic?, mai ?nt?i se execut? codul destructorului clasei derivate, apoi se apeleaz? destructorul clasei de baz?.

Construc?ia luat? ?n [ ] ?nseamn? c? apelul explicit al constructorului de baz? poate fi omis, ?i ?n acest caz compilatorul va genera codul de apelare a constructorului de baz? f?r? argumente. ?ns?, nimic nu ne ?mpiedic? s? scriem explicit apelul f?r? argumente al constructorului de baz?.

Definim un constructor ?n clasa derivat? rational_fara_semn. Pentru aceasta, inser?m urm?torul cod dup? sec?iunea protected ?n defini?ia acestei clase din exemplul precedent:

public:
rational_fara_semn(unsigned pi_init=0,
                   unsigned n_init=0, unsigned m_init=1):
            fractie_rationala(n_init, m_init)//apel?m constructorul bazei
 {
   part_int = pi_init; // ini?ializ?m c?mpul ad?ugat ?n
                       // clasa derivat?
 }

Constructorul clasei rational_fara_semn are trei parametri (?n mod normal, ace?tia sunt valorile ini?iale, respectiv, pentru partea ?ntreag?, num?r?torul ?i numitorul). Pentru to?i trei am indicat ?i valorile implicite: 0 – pentru partea ?ntreag? ?i numitor ?i 1 – pentru num?r?tor. Subliniem c? valorile implicite pentru num?r?tor ?i numitor ?n clasa derivat? coincid cu valorile implicite pentru aceste c?mpuri la clasa de baz?. ?n principiu, acesta nu este obligatoriu, dar pentru integritatea proiectului nostru a?a va fi mai bine.

Acum avem posibilitatea de a ini?ializa explicit obiectele rational_fara_semn. Pentru a aduce un exemplu mai ilustrativ de un a?a tip de ini?ializare, mai supra?nc?rc?m operatorul de inser?ie <<. Cu acest scop ad?ug?m la codul precedent urm?torul cod:

friend ostream &operator<<(ostream& stream, const rational_fara_semn &rfs) 
{  if(rfs.part_int > 0) stream << rfs.part_int << ",";
         stream << rfs.numarat << "/" << rfs.numit;
 return stream;
}

Adic?, am ad?ugat ?n defini?ia clasei rational_fara_semn supra?nc?rcarea operatorului << ?n forma de func?ia friend, care afi?eaz? (transmite ?n stream) partea ?ntreag?, o virgul?, num?r?torul, caracterul / (slash) ?i numitorul. Partea ?ntreag? ?i virgula vor fi afi?ate numai ?n cazul c?nd partea ?ntreag? este diferit? de zero.

Re?ine?i cum la un obiect rational_fara_semn (parametrul rfs) se face adresarea c?tre c?mpurile numarat ?i numit, de parc? ele au fost declarate ?n defini?ia acestei clase. ?n realitate, ele au fost mo?tenite de la clasa de baz? fractie_rationala.

?nlocuim ?n func?ia main() din programul precedent instruc?iunea:

rational_fara_semn a, b;

cu:

rational_fara_semn a(2, 1, 3), b(0, 3, 5);

?i lans?m programul. Rezultatul afi?at de data aceasta va fi:

a=2,1/3
b=3/5

Constructorul definit ?n clasa derivat? a ini?ializat toate c?mpurile obiectelor a ?i b. Acum, la afi?area obiectelor a ?i b a lucrat func?ia operator<<() supra?nc?rcat? pentru clasa derivat? ?i a afi?at doi ?ntregi ?i o treime pentru obiectul a ?i trei cincimi pentru obiectul b.

Acela?i lucru ?l facem ?i pentru clasa rational. Anume: inser?m ?n declararea acestei clase urm?torul cod:

public:
 rational(int pi_init=0, unsigned n_init=0, 
          unsigned m_init=1):rational_fara_semn(pi_init>=0 ? 
            (semn='+',pi_init):(semn='-',-pi_init), n_init, m_init)
 {
 
 }
 
 friend ostream &operator<<(ostream& stream, const rational &r)
 {
   if(r.semn=='-')
      stream << '-';
   stream << rational_fara_semn(r.part_int, r.numarat, r.numit);
   return stream;
 }

care reprezint? definirea constructorului ?i supra?nc?rcarea operatorului << pentru clasa rational. Am p?strat la constructor trei parametri, ?ns? primul parametru este acum de tipul int, ceea ce ne va permite s? indic?m semnul num?rului ra?ional. O alt? posibilitate era de a introduce patru parametri, primul fiind destinat numai pentru indicarea semnului num?rului. ?n fine, putem supra?nc?rca ambele versiuni ale constructorului: una cu trei parametri ?i cea de a doua cu patru.

Versiunea cu trei parametri este mai comod?, ?ns? are un neajuns, fiindc? mic?oreaz? de dou? ori intervalul de ini?ializare al p?r?ii ?ntregi. ?n acest exemplu este reprezentat? versiunea constructorului cu trei parametri. Deci, primul parametru aduce informa?ii despre semnul num?rului ra?ional ?i despre valoarea absolut? a p?r?ii ?ntregi a acestui num?r.

Re?ine?i cum ?n acest exemplu se apeleaz? constructorul clasei de baz?. Mai mult, expresia pentru primul argument al lui. Aceast? expresie reprezint? operatorul ternar ?:.

Dac? primul argument al constructorului derivat nu este negativ, atunci ?n c?mpul nou ad?ugat semn se memorizeaz? valoarea '+' ?i primul argument la apelul constructorului de baz? va fi valoarea pozitiv? pi_init. ?n cazul c?nd primul argument al constructorului derivat este negativ, atunci ?n c?mpul nou ad?ugat semn se memorizeaz? valoarea '-', iar primul argument la apelul constructorului de baz? va fi valoarea pozitiv? -pi_init.

Am apelat constructorul clasei de baz?, totodat? am ini?ializat c?mpul semn. De aceea, ?nsu?i corpul constructorului derivat s-a primit gol. Supra?nc?rcarea operatorului << pentru clasa derivat? rational nu este greu de ?n?eles. Re?ine?i cum ?n el a fost apelat acela?i operator pentru clasa de baz?. Pentru verificare inser?m ?n func?ia main() urm?torul fragment de cod:

rational r1(-1, 3, 7), r2(2, 1, 3);
cout << "r1=" << r1 << endl;
cout << "r2=" << r2 << endl;

Textul afi?at de el va fi:

r1=-1,3/7
r2=2,1/3

Versiunea examinat? a constructorului cu trei parametri pentru clasa rational mai are ?nc? un neajuns. Cu acest constructor nu putem ini?ializa numere  ra?ionale negative  din  intervalul (-1, 0). ?ntr-adev?r, cum este posibil de creat, de exemplu, obiectul cu valoarea -1/2? Dac? ?ncerc?m s? scriem:

cout << rational(-0, 1, 2) << endl;

vom ob?ine:

1/2

Ceea ce nu este corect. De aceea, inser?m ?n sec?iunea public a clasei rational ?nc? un constructor:

rational(char semn_init, unsigned pi_init,
         unsigned n_init=0, unsigned m_init=1):
         rational_fara_semn(pi_init, n_init, m_init)
{
 semn = semn_init;
}

Acum avem posibilitatea s? scriem:

cout << rational(’-’, 0, 1, 2) << endl;

cu rezultatul:

-1/2

Este clar c? parametrul semn_init poate fi sau ’-’ sau ’+’. Verificarea acestui fapt, pentru simplicitate, a fost omis?.
_________________________
Autorul: dr.conf. S. Pereteatcu

7.4. Definirea destructorului ?n clasa derivat?

7.4. Definirea destructorului ?n clasa derivat?

Forma general? de definire a destructorului ?n clasa derivat? arat? astfel:

[nume_clas?_derivat?::]~nume_clas?_derivat?(){

   // codul destructorului clasei derivate

   …

}

La definirea destructorului ?n clasa derivat? nu trebuie de apelat explicit destructorul clasei de baz? (va fi o eroare dac? ve?i ?ncerca s? face?i acest lucru). Compilatorul insereaz? el ?nsu?i codul ce apeleaz? destructorul clasei de baz? dup? executarea codului destructorului clasei derivate.
_________________________
Autorul: dr.conf. S. Pereteatcu
 

7.5. Atribuirea obiectelor de la clasa derivat? la clasa de baz?

7.5. Atribuirea obiectelor de la clasa derivat? la clasa de baz?

La atribuirea obiectelor prin operatorul de atribuire implicit, c?t ?i prin unul supra?nc?rcat, exist? urm?toarea lege:

Se admite orice atribuire a obiectului de baz? oric?rui obiect derivat (nu depinde de genera?ie). Unica excep?ie, c?nd operatorul de atribuire a fost supra?nc?rcat ?n clasa de baz? ca membru privat.Atribuirea invers?, adic? a obiectului derivat unui obiect de baz?, nu este permis?.

Direc?ia admis? de atribuire ?n regula dat? este foarte u?or de ?n?eles. Obiectul care se atribuie trebuie s? fie ?n stare s? completeze toate c?mpurile obiectului c?ruia i se atribuie. La mo?tenire, clasa derivat? mo?tene?te toate c?mpurile str?mo?ilor s?i ?i, deci, ea este ?n stare s? completeze c?mpurile oric?rui str?mo? la atribuire.

?n clasa derivat? poate s? apar? c?mpuri noi, care nu existau la str?mo?ii s?i ?i pe care nici un str?mo? nu va fi ?n stare s? le completeze, de aceea direc?ia de atribuire de la str?mo? spre un derivat este interzis?. Urm?torul fragment, care trebuie de ad?ugat la func?ia main(), demonstreaz? cele spuse:

fractie_rationala f1, f2;
   f1=a;  // atribuirea de la rational_fara_semn la
          // fractie_rationala
   f2=r1; // atribuirea de la rational la
          // fractie_rationala
   b=r1;  // atribuirea de la rational la
          // rational_fara_semn
   cout << "f1=" << f1 << endl;
   cout << "f2=" << f2 << endl;
   cout << "b=" << b << endl;

El va afi?a urm?toarea ie?ire:

f1=1/3
f2=3/7
b=1,3/7

Urm?toarele atribuiri sunt interzise ?i nu vor fi compilate:

a=f1;
r1=f2;
r1=b;

Direc?ia de atribuire admis? ?ntre pointeri spre clase de baz? ?i spre cele derivate este aceea?i ca ?i pentru obiecte:

Este admis? atribuirea pointerului spre o clas? de baz? a adresei oric?rui obiect al clasei derivate sau a valorii pointerului spre obiectele claselor derivate (nu depinde de tipul mo?tenirii: nemijlocit? sau prin alte clase).Atribuirea invers?, adic? atribuirea pointerului spre o clas? derivat? a adresei unui obiect al clasei de baz? sau a valorii unui alt pointer spre o clas? de baz?, nu se admite.

Deci, direc?ia admis? de atribuire este de la pointeri cu tipul adresa clasei derivate (?n orice genera?ie) spre pointer cu tipul adresa clasei de baz?. Pentru exemplu, cercet?m urm?torul fragment, pe care ?l ad?ug?m la func?ia main():

fractie_rationala *p;
   rational_fara_semn *q;
   rational *t;
 
   p=&a; // pointerului spre fractie_rationala se atribuie
         // adresa unui rational_fara_semn
   cout << "*p=" << *p << endl; 
   q=&b;
   p=q; // pointerului spre fractie_rationala se atribuie un
        // pointer spre rational_fara_semn
   cout << "*p=" << *p << endl;
 
   p=&r1; // pointerului spre fractie_rationala – adresa
          // unui rational
   cout << "*p=" << *p << endl;
   t=&r2;
   p=t; // pointerului spre fractie_rationala – pointer spre
        // rational
   cout << "*p=" << *p << endl;
 
   q=&r1; // pointerului spre rational_fara_semn – adresa
          // unui rational
   cout << "*q=" << *q << endl;
   t=&r2;
   q=t; // pointerului spre rational_fara_semn – pointer spre
        // rational
   cout << "*q=" << *q << endl;

Atribuirile care ne intereseaz? ?n acest fragment sunt comentate. Fragmentul ad?ugat va afi?a:

*p=1/3
*p=3/7
*p=3/7
*p=1/3
*q=1,3/7
*q=2,1/3

Aceste rezultate sunt clare. Totu?i, se cere o observa?ie ?n compara?ie cu rezultatele din exemplul precedent. C?nd atribuim obiectului f1 obiectul a cu valoarea 2,1/3, f1 devine 1/3, expresia cout << f1 afi?eaz? 1/3, ceea ce este corect.

C?nd pointerului p cu tipul de baz? fractie_rationala ?i atribuim &a, el ob?ine adresa obiectului a, deci, pointeaz? un obiect rational_fara_semn cu valoarea 2,1/3, iar expresia cout << p totuna va afi?a 1/3, ceea ce nu este corect. Vom am?na discutarea acestei probleme p?n? la cercetarea func?iilor virtuale.

Urm?toarele atribuiri sunt interzise ?i nu vor fi compilate:

q=&f1;
q=p;
t=&f1;
t=p;
t=&a;
t=q;

_________________________
Autorul: dr.conf. S. Pereteatcu

Translate Переводчик

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

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

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