10.2Probleme propuse
Problema 1. Să se definească o clasă Baza care conţine două variabile de tip double cu specificatorul de acces protected. Clasa va defini un constructor cu parametri şi un constructor implicit, fără parametri. Se va defini o clasă Complex ce va implementa numere complexe. Clasa Complex va moşteni clasa Baza şi va defini un constructor cu parametri, un constructor implicit fără parametri, o funcţie care prescrie valorile părţilor reală şi imaginară a numărului complex şi o funcţie care scrie pe ecran numărul complex sub forma
(partea reală, partea imaginară)
Se va defini o funcţie globală care să adune douǎ numere complexe.
Funcţii virtuale. Polimorfism. Supraîncărcarea operatorilor
10.3Funcţii virtuale. Polimorfism
Fie de exemplu o clasă Baza şi o clasă derivată Deriv. Definim o funcţie f() în clasa Baza şi o funcţie cu acelaşi nume f() în clasa Deriv
class Baza
{
public :
void f() {cout << " se apeleaza Baza :: f()" << endl;}
};
şi o clasă derivată Deriv
class Deriv : public Baza
{
public :
void f() {cout << " se apeleaza Deriv :: f()" << endl;}
};
Un pointer de tipul clasei de bază poate conţine adresele unor obiecte de tipul clasei de bază şi al claselor derivate.
Considerăm următoarea funcţie main() în care definim un vector de pointeri ptr de tipul clasei Baza în care memorăm adresa unui obiect de tipul Baza şi adresa unui obiect de tipul Deriv. Apelăm funcţia f() prin pointer pentru cele două obiecte.
void main()
{
Baza * ptr [2];
Baza b ;
ptr[0] = &b ;
Deriv d ;
ptr[1] = &d ;
for(int i = 0; i < 2; i++)
ptr->f() ;
}
La execuţia acestui program se vor afişa mesajele
se apelează Baza :: f()
se apelează Baza :: f()
O instrucţiune de apelare a funcţiei f() prin pointer duce la apelarea funcţiei f() definită în clasa Baza deoarece funcţia apelată este determinată de tipul pointerului şi nu de tipul obiectului indicat de pointer.
Vom transforma funcţia f() în funcţie virtuală adăugând cuvântul virtual definiţia funcţiei f().
virtual void f() {cout << " se apelează Baza :: f()" << endl;}
virtual void f() {cout << " se apeleaza Deriv :: f()" << endl;}
La execuţia programului se vor afişa mesajele
se apelează Baza :: f()
se apelează Deriv :: f()
În cazul funcţiilor virtuale funcţia apelată este determinată de tipul obiectului indicat de pointer.
Probleme rezolvate
Problema 1. Să se definească o clasă de bază numită Persoana ce conţine un câmp nume cu numele persoanei şi o funcţie print() ce afişază numele. Funcţia print() va fi virtuală. Definiţia clasei este cea de mai jos.
class Persoana
{
protected:
string name;
public:
Persoana(string s) { name = s;}
virtual void print()
{cout << "nume : " << name << "\n";}
};
Să se definească o clasă numită Angajat ce moşteneşte clasa Persoana. Clasa va conţine un câmp cu numele departamentului şi o funcţie print() ce va afişa numele angajatului şi numele departamentului. Definiţia clasei este cea de mai jos.
class Angajat : public Persoana
{
protected:
string dept;
public:
Angajat(string s, string d) : Persoana(s)
{dept = d;}
virtual void print()
{Persoana::print(); cout << " departament : " << dept << "\n"; }
};
Să se definească o clasă numită Manager ce moşteneşte clasa Angajat. Clasa va conţine un câmp cu poziţia managerului şi o funcţie print() ce va afişa numele, departamentul şi poziţia managerului. Definiţia clasei este cea de mai jos.
class Manager : public Angajat
{
protected:
string position;
public:
Manager(string s, string d, string p) : Angajat(s, d)
{position = p;}
virtual void print()
{Angajat::print();cout << " pozitia : " << position << "\n"; }
};
Se vor crea obiecte cu angajaţi şi manageri şi se vor afişa datele acestora (numele, departamentul şi poziţia managerială). Programul de rezolvare a problemei este prezentat mai jos.
int main()
{
Angajat x1(“Alex”, “proiectare”);
Manager m1(“George”, “proiectare”, “sef”);
Persoana * ps[2];
ps[0] = &x1;
ps[1] = & m1;
// scrie datele pentru x1 si m1
for(int i = 0; i < 2; i++)
ps[i]->print();
return 0;
}
Observaţii. Se va explica de ce funcţia print() trebuie să fie virtuală.
Rezultatele rulării programului sunt cele de mai jos.
10.4Supraîncărcarea operatorilor
Operatorii limbajului C++ sunt automat definiţi pentru tipurile fundamentale: int, double, char, etc. Când definim o clasă nouă, creăm un nou tip. Operatorii limbajului pot fi definiţi şi pentru tipurile nou create. Această operaţie se numeşte supraîncărcarea operatorilor (operator overloading).
Supraîncărcarea operatorilor aritmetici
Pentru a supraîncarca un operator definim o functie de forma
tip operator semn (parametri) {/* corpul funcţiei */}
unde semn este operatorul dorit +,-, *, / , [], (), <<, >>, =, etc. Operatorii supraîncărcaţi au aceeaşi prioritate ca cei originali şi acelaşi număr de parametri. Menţionăm că orice operator aritmetic supraîncărcat poate fi definit ca funcţie membră a clasei sau ca funcţie globală. Exemple de supraîncărcare a operatprilor aritmetici sunt prezentate în problemele rezolvate.
Supraîncărcarea operatorilor << şi >>
Operatorul << este numit operator de inserţie, el insereaza caractere într-un stream. Operatorul >> este numit operator de extracţie, el extrage caractere dintr-un stream.
Toate funcţiile de inserţie au forma
ostream& operator << (ostream& stream, tip obiect)
{
// corpul functiei
return stream;
}
Primul parametru este o referinţă la streamul de ieşire. Al doilea este obiectul ce trebuie inserat. Ultima instrucţiune este
return stream;
Operatorul de extracţie >> este supraîncărcat astfel
friend istream& operator >> (istream& stream, tip& obiect);
{
// corpul functiei
return stream;
}
Operatorii << şi >> pe care i-am definit nu pot fi membri ai clasei deoarece operandul stâng ostream, respectiv istream nu este un membru al clasei. În consecinţă, aceşti operatori vor fi funcţii externe.
Probleme rezolvate
Problema 2. Se va construi o clasă Line pentru lucrul cu funcţii liniare,
f(x) = m x + n
m şi n sunt memorate în două variabile de tip double, denumite m şi n. Se vor defini :
-
un constructor fără parametri (default), ce va crea o funcţie liniară iniţializată la valoarea (0, 0),
-
un constructor tip copiere,
-
un operator + ca funcţie globală,
-
un operator = ca funcţie membră a clasei,
-
un operator * (compunere a două funcţii), ca funcţie globală,
-
un operator << ca funcţie globală.
Fie două funcţii liniare :
f1(x) = m1 x + n1
f2(x) = m2 x + n2
Operatorul + adună cele două funcţii liniare. Rezultatul lui este
f3(x) = m3 x + n3 = (m1 + m2) x + (n1 + n2)
Operatorul * compune cele două funcţii liniare
f3(x) = f1(f2(x)) = m1(m2 x + n2) + n1 = m1 m2 x + m1 n2 + n1
Programul de rezolvare a problemei este cel de mai jos.
#include
#include
using namespace std;
class Line
{
private:
double m, n;
public:
void print();
Line();
Line(double, double);
Line operator*(Line);
Line& operator=(const Line &);
friend Line operator+( Line x, Line y);
friend ostream& operator << (ostream& stream, Line x);
};
Line::Line()
{
m = 0;
n = 0;
}
Line::Line(double a, double b)
{
m = a;
n = b;
}
Line operator+( Line x, Line y)
{
Line temp(x.m + y.m, x.n + y.n);
return temp;
}
Line Line::operator*( Line x)
{
Line temp(m * x.m, n + m * x.n);
return temp;
}
Line & Line::operator = (const Line & r)
{
// obiectul ce a apelat operatorul primeste valoarea obiectului r
m = r.m;
n = r.n;
// rezultatul functiei este referinta obiectului ce a apelat operatorul
return *this;
}
ostream& operator << (ostream& stream, Line x)
{
stream << "(" << x.m << "," << x.n << ")";
return stream;
}
int main(int argc, char *argv[])
{
Line f1(1.5, -2.5), f2(2.0, 3.0);
Line f3, f4, f5;
cout << "f1 = " << f1 << endl << "f2 = " << f2 << endl;
f3 = f1 + f2;
cout << "f1 + f2 = " << f3 << endl;
f4 = f1 * f2;
cout << "f1(f2) = " << f4 << endl;
return 0;
}
Rezultatele rulării programului sunt cele de mai jos.
Dostları ilə paylaş: |