Etichete: POO

Generalități

Supraîncărcarea funcțiilor (eng. functions overloading) se referă la posibilitatea ca în același program să fie declarate și definite mai multe funcții cu același nume, care să difere prin parametri. La apelul unei funcții supraîncărcate, compilatorul stabilește care dintre declarații se potrivește cu apelul respectiv, comparând numărul și tipul parametrilor actuali cu cel al parametrilor formali.

Metodele unei clase, fiind funcții, pot fi supraîncărcate. Totodată, în interiorul unei clase pot fi definiți și supraîncărcați operatori (+, -, *, etc.), care permit utilizarea naturală a obiectelor, în raport cu înțelesul lor.

De exemplu, fracțiile pot fi adunate, scăzute, etc. O clasa care implementează fracții poate fi îmbogățită cu operațiile naturale de adunare, scădere, înmulțire, împărțire, dar și cu comparații, incrementări/decrementări, citire, afișare, etc.

În acest fel lucrul cu obiecte devine natural, evitând apelul explicit al unor metode care poate părea forțat, în anumite situații.

Supraîncărcarea metodelor

De știut:

  • în definiția unei clase putem avea mai multe metode cu același nume;
  • ele trebuie să difere prin numărul și tipul parametrilor, nu prin tipul rezultatului;
  • nu se pot supraîncărca funcții care diferă numai prin tipul rezultatului!

Exemplu

În exemplul următor, definim pentru clasa Fracție două metode creste():

  • una fără parametri, care va mări valoarea fracției încapsulate în obiect cu 1;
  • una cu un parametru întreg n, care va mări valoarea fracției încapsulate în obiect cu n.
#include <iostream>

using namespace std;

class Fractie{
    private:
        int numarator, numitor;
    public:
		void afiseaza() /// metodă pentru afișarea fractiei
		{
			cout << numarator << "/" << numitor << endl;
		}
		Fractie(int a , int b) /// constructor
		{
			if(b < 0)
				a = -a, b = -b;
			numarator = a, numitor = b;
		}
		Fractie & creste()
		{
			numarator += numitor;
			return *this;
		}
		Fractie & creste(int n)
		{
			numarator += n * numitor;
			return *this;
		}
};

int main(){
	Fractie x(1 , 4);
	x.afiseaza();
	x.creste();
	x.afiseaza();
	x.creste(3);
	x.afiseaza();
	return 0;
}

Programul de mai sus va afișa:

1/4
5/4
17/4

Supraîncărcarea operatorilor

Putem supraîncărca aproape toți operatorii C++ predefiniți. În acest mod putem folosi operatorii și pentru tipurile definite de noi, nu doar pentru cele predefinite.

Nu putem supraîncărca operatori pentru tipurile de bază: int, double, etc.

Nu putem supraîncărca operatori care nu există. De exemplu, nu putem adăuga operatorul #.

Nu putem schimba prioritatea operatorilor.

Operatorii sunt funcții cu un nume special: cuvântul rezervat operator, urmat de simbolul operatorului pe care îl definim. La fel ca orice altă funcție, și operatorii au tip al rezultatului și listă de parametri.

Operatorii care pot fi supraîncărcați sunt:

  • +, -, *, /, %
  • ^, &, |, ~,
  • ,
  • =
  • <, >, <=, >=, ==, !=,
  • !, &&, ||
  • ++, --
  • <<, >>,
  • +=, -=, /=, %=, ^=, &=, |=, *=, <<=, >>=,
  • [], ()
  • ->, ->*
  • new, new [], delete, delete []

Următorii operatori nu pot fi supraîncărcați:

  • ::, .*, ., ?:

Sunt două modalități de defini operatori pentru o clasă:

Putem folosi metodele când operatorul este unar, sau când este binar și primul operand este obiect al clasei pentru care definim operatorul. Dacă operatorul este binar și primul operator nu este obiect al clasei, vom defini operatorul prin intermediul funcțiilor prietene.

Exemplu

În programul de mai jos definim o clasă Fractie în care vom defini și supraîncărca operatorul + pentru a implementa adunarea fracțiilor și adunarea fracțiilor cu întreg. Distingem următoarele cazuri:

  • operația F + G, unde F și G sunt fracții, poate fi implementată atât printr-o metodă cât și ca funcție prietenă;
  • operația F + n, unde F este fracție și n este număr întreg, poate fi implementată atât printr-o metodă cât și ca funcție prietenă;
  • operația n + F, unde F este fracție și n este număr întreg, va fi implementată printr-o funcție prietenă.

Situația de mai sus se datorează faptului că, la implementarea operatorului ca metodă, obiectul curent, pentru care se implementează metoda, este primul operand.

#include <iostream>

using namespace std;

class Fractie{
    private:
        int numarator, numitor;
    public:
		void afiseaza() /// metodă pentru afișarea fractiei
		{
			cout << numarator << "/" << numitor << endl;
		}
		Fractie(int a = 0, int b = 1) /// constructor
		{
			if(b < 0)
				a = -a, b = -b;
			numarator = a, numitor = b;
		}
		Fractie operator+ (Fractie F)
		{
			Fractie R;
			R.numarator = numarator * F.numitor + numitor * F.numarator;
			R.numitor = numitor * F.numitor;
			return R;
		}
		Fractie operator+ (int n)
		{
			Fractie R;
			R.numarator = numarator + n * numitor;
			R.numitor = numitor;
			return R;
		}
		friend Fractie operator + (int n, Fractie F)
		{
			Fractie R;
			R.numarator = F.numarator + n * F.numitor;
			R.numitor = F.numitor;
			return R;
		}
};

int main(){
	Fractie X(1 , 4), Y(2, 3);
	Fractie R = X + Y;
	R.afiseaza();
	R = X + 2;
	R.afiseaza();
	R = 2 + X;
	R.afiseaza();
	return 0;
}

Programul de mai sus va afișa:

11/12
9/4
9/4

Citește mai departe