16.8.1. Efectuarea conversiilor
În mod automat în limbajele C şi C++ se aplică o conversie în următoarele trei situaţii:
-
dacă un operator se referă la operanzi de tipuri diferite;
-
dacă tipul parametrului actual al unei funcţii diferă de tipul parametrului formal corespunzător;
-
dacă tipul valorii returnate de o funcţie, tip specificat în antetul funcţiei, diferă de tipul expresiei din instrucţiunea return.
În primul caz, dacă operatorul nu este de atribuire, se va folosi regula conversiilor implicite. Deci mai întâi tipurile char şi enum se convertesc în int, după care tipurile "inferioare" se convertesc spre cele "superioare" (de exemplu int în double). Dacă operatorul este de atribuire, atunci tipul expresiei din partea dreaptă se converteşte spre tipul expresiei din partea stângă. În cazul al doilea tipul parametrului actual se converteşte spre tipul parametrului formal. În cazul al treilea tipul expresiei din instrucţiunea return se converteşte spre tipul specificat în antetul funcţiei.
16.8.2. Conversii implicite definite de programator
Considerăm următorul exemplu referitor la clasa numerelor raţionale. Clasa va avea două date membru de tip long pentru memorarea numărătorului şi numitorului. Obiectele clasei se vor iniţializa printr-un constructor. Se va supraîncărca operatorul ~, care va efectua simplificarea fracţiei, şi operatorul *, pentru înmulţirea a două fracţii. Deasmenea, se va defini o funcţie membru afiseaza, pentru afişarea unei fracţii. Fişierul conv2.cpp:
Observăm că operatorul de înmulţire se poate folosi numai pentru înmulţirea a două numere raţionale. Deci în cazul unei expresii de forma următoare
z = x * 4;
compilatorul ar semnala o eroare. Totuşi ar fi de dorit ca expresiile de forma de mai sus, să fie corecte. Pentru a realiza acest lucru, trebuie definită o conversie implicită din tipul
long în tipul
fractie.
În general, conversiile dintr-un tip standard într-un tip abstract se realizează cu ajutorul unui constructor al tipului abstract. Constructorul trebuie să aibă un parametru, care aparţine tipului standard, iar dacă sunt şi alţi parametri, ei trebuie să fie iniţializaţi.
În cazul de mai sus constructorul, care realizează conversia din tipul long în tipul fractie, poate fi declarat sub forma:
fractie(long a, long b = 1);
sau
fractie(long a = 0, long b = 1);
sau
fractie(long a);
De exemplu, dacă fişierul
conv2.cpp se modifică astfel încât constructorul să fie declarat în forma:
fractie(long a = 0, long b = 1);
atunci expresia
z = x * 4;
este corectă şi se obţine numărul raţional 8 / 5. Al doilea operand al operatorului de înmulţire trebuie să aparţină tipului fracţie. Cu ajutorul constructorului se va crea un obiect anonim fractie(4, 1), care se va folosi în locul operandului al doilea. Menţionăm, că de fapt s-au făcut două conversii. Constanta 4 este de tipul int, deci la apelarea constructorului s-a făcut o conversie de la tipul int la tipul long, după care s-a făcut o conversie din tipul long în tipul fractie, prin crearea obiectului anonim.
Totuşi compilatorul semnalizează o eroare la întâlnirea unei expresii de forma următoare:
z = 4 * x;
Explicaţia este, că în cazul expresiei x * 4, se apelează funcţia membru operator* pentru obiectul x. Însă în cazul expresiei 4 * x, se încearcă apelarea unei funcţii membru (operatorul de înmulţire) pentru constanta 4, ceea ce conduce la eroare.
O soluţie ar putea fi ca operatorul de înmulţire să se defineasă printr-o funcţie prieten, de exemplu în modul următor:
class fractie {
long numarator;
long numitor;
public:
...
friend fractie operator*(fractie p, fractie q);
...
};
fractie operator*(fractie p, fractie q)
{
return ~fractie(p.numarator*q.numarator,
p.numitor*q.numitor);
}
Dacă operatorul de înmulţire se defineşte în modul de mai sus, atunci ambele expresii x * 4 şi 4 * x vor fi corecte. Dezavantajul acestei metode este, că prin utilizarea unei funcţii prieten, gradul de protecţie a datelor scade. În continuare prezentăm o altă metodă de înlăturare a erorii de mai sus, astfel încât să nu fie nevoie de introducerea unei funcţii prieten. Vom defini o funcţie membru inmultire, care se va apela de către funcţia, care supraîncarcă operatorul de înmulţire.
class fractie {
long numarator;
long numitor;
public:
...
fractie inmultire(fractie r);
...
};
inline fractie fractie::inmultire(fractie r)
{
return ~fractie(numarator*r.numarator,
numitor*r.numitor);
}
fractie operator*(fractie p, fractie q)
{
return p.inmultire( q );
}
Observăm că şi în acest caz, ambele expresii sunt corecte şi nu s-a folosit funcţie prieten.
Definirea adecvată a constructorului poate să conducă şi la conversii dintr-un tip abstract într-un alt tip abstract. Prezentăm un exemplu pentru măsurarea unei lungimi, în care sunt definite două clase, folosind două unităţi de măsură diferite. Cu ajutorul clasei Lungime_m se va memora lungimea în metri (mm, cm, dm şi m), iar folosind clasa Lungime_inch memorarea lungimii se va realiza în inch (line, inch, foot şi yard). Relaţiile dintre cele două unităţi de măsură sunt următoarele:
1 line=2.54 mm1 inch=10 lines=2.54 cm1 foot=12 inches=30.48 cm1 yard=3 feet=91.44 cm
Deasemenea, programul va realiza conversia din inch în metri. Fişierul lung1.cpp:
Observăm, că şi în acest caz s-a folosit un constructor pentru realizarea conversiei din tipul Lungime_inch în tipul Lungime_m. Clasa Lungime_inch s a declarat înainte de clasa Lungime_m, deoarece constructorul, care realizează conversia, foloseşte tipul Lungime_inch.
16.8.3. Supraîncărcarea operatorului de conversie explicită
Conversia dintr-un tip abstract într-un tip standard se poate realiza prin supraîncărcarea operatorului de conversie explicită. Pentru a realiza conversia din tipul abstract Clasa în tipul standard tip_standard este necesară o funcţie membru de forma următoare:
class Clasa {
...
public:
...
operator tip_standard(); // declaraţie
...
};
Clasa::operator tip_standard()
{
...
return expresie; // se returnează o expresie având tipul
// tip_standard
}
Obiectul de tip Clasa se va converti în valoarea expresiei returnate de către funcţia membru, care supraîncarcă operatorul de conversie explicită. Menţionăm că în declaraţia funcţiei membru, care supraîncarcă operatorul de conversie explicită, nu trebuie specificat tipul, care se returnează, deoarece acest tip va fi întotdeauna tipul standard la care se face conversia. Dacă se încearcă specificarea tipului returnat, atunci compilatorul va semnala eroare. În următorul exemplu se va efectua conversia unui număr raţional într un număr de tip double. Fişierul conv3.cpp: