Difference between revisions of "Supraîncărcarea operatorilor în limbajul C-plus-plus"
From linux360
Radubolovan (talk | contribs) |
(→Exemplu: Adapted to GeSHiCodeTag) |
||
(21 intermediate revisions by 13 users not shown) | |||
Line 1: | Line 1: | ||
− | + | ''Titlul oribil este rezultatul unei limitări in Wiki -- imediat ce se rezolvă, vom face şi titul mai uşor de privit'' | |
− | De ce am avea nevoie de asa ceva? Dupa cum se stie se poate crea o functie a unei clase care sa faca exact ce ar face un operator si care sa o definim (de exemplu) '''suma'''. Atunci am apela acea metoda astfel : | + | ---- |
+ | |||
+ | == Introducere == | ||
+ | Operatiile de baza sunt implementate in limbajul C++ cu ajutorul operatorilor. In limbajul C++ operatorii sunt, de fapt, functii. Operatorii oferiti de limbaj pot ''lucra'' decat asupra tipurilor de date predefinite. Pentru a folosi operatorii asupra tipurilor de date construite de noi, limbajul C++ ne ofera un mecanism care se numeste '''supraincarcarea operatorilor'''. | ||
+ | |||
+ | De ce am avea nevoie de asa ceva? Dupa cum se stie se poate crea o functie a unei clase care sa faca exact ce ar face un operator si care sa o definim (de exemplu) '''suma'''. Atunci am apela acea metoda astfel: | ||
Obiect ob1; | Obiect ob1; | ||
Line 7: | Line 12: | ||
Obiect ob3 = ob1.suma( ob2 ); | Obiect ob3 = ob1.suma( ob2 ); | ||
− | Cam greoi! | + | Cam greoi! Ar fi mai simplu daca am scrie aşa: |
Obiect ob3 = ob1 + ob2; | Obiect ob3 = ob1 + ob2; | ||
− | |||
Astfel s-a ajuns la nevoia de a supraincarcarea operatorii! | Astfel s-a ajuns la nevoia de a supraincarcarea operatorii! | ||
− | Supraincarcarea metodelor claselor in general si a operatorilor in particular sunt doua dintre cele mai importante mecanisme ale limbajului C++. Ele sunt un pas urias catre polimorfism (asta este alta bazaconie inventata de oamenii cu creiere luminate). Aceste | + | == Teorie == |
+ | |||
+ | Supraincarcarea metodelor claselor in general si a operatorilor in particular sunt doua dintre cele mai importante mecanisme ale limbajului C++. Ele sunt un pas urias catre polimorfism (asta este alta bazaconie inventata de oamenii cu creiere luminate). Aceste ''oportunitati'' sunt cele care ofera flexibilitate aplicatiilor pe care le construim. | ||
+ | |||
Iata lista operatorilor din C++ care se pot supraincarca: | Iata lista operatorilor din C++ care se pot supraincarca: | ||
− | + | * new delete | |
− | + | * () [] | |
− | + | * + - * / % | |
− | + | * ^ & | ~ | |
− | + | * ! = < > | |
− | + | * += -= *= /= %= | |
− | + | * ^= &= |= | |
− | + | * << >> | |
− | + | * >>= <<= | |
− | + | * == != <= >= | |
− | + | * && || | |
− | + | * ++ -- | |
− | + | * , | |
− | + | * ->* | |
− | + | * -> | |
− | + | unde operatorul '''()''' este apelul unei functii, iar operatorul '''[]''' este operatorul de indexare. | |
Urmatorii operatori nu se pot supraincarca: | Urmatorii operatori nu se pot supraincarca: | ||
− | + | * . .* :: ?: sizeof | |
+ | |||
Reguli care trebuie sa fie respectate in supraincarcarea operatorilor: | Reguli care trebuie sa fie respectate in supraincarcarea operatorilor: | ||
− | + | * operatorii =, (), [], si -> trebuie sa fie membrii nestatici ai clasei | |
− | + | * operatorul = nu poate fi mostenit | |
− | + | * operatorii pentru tipurile predefinite ale limbajului nu se pot supraincarca | |
− | + | * operatorii nu pot avea argumente implicite | |
+ | |||
Operatorii pentru un anumit tip definit de utilizator (clasa) pot sa fie sau nu membru al clasei. In cazul in care se supraincarca un operator pentru o clasa, dar acel operator nu apartine clasei, trebuie sa fie declarat '''friend''' in clasa respectiva si sa aiba cel putin un argument de tipul clasei respective. Exceptie de la aceasta regula fac operatorii '''= () [] ->''' care nu pot fi supraincarcati folosind functii de tipul friend ale unei anumite clase. | Operatorii pentru un anumit tip definit de utilizator (clasa) pot sa fie sau nu membru al clasei. In cazul in care se supraincarca un operator pentru o clasa, dar acel operator nu apartine clasei, trebuie sa fie declarat '''friend''' in clasa respectiva si sa aiba cel putin un argument de tipul clasei respective. Exceptie de la aceasta regula fac operatorii '''= () [] ->''' care nu pot fi supraincarcati folosind functii de tipul friend ale unei anumite clase. | ||
+ | |||
+ | == Exemplu == | ||
Un exemplu: voi da ca exemple supraincarcarea operatorilor ++ pre si post indexat. | Un exemplu: voi da ca exemple supraincarcarea operatorilor ++ pre si post indexat. | ||
− | class MyPoint | + | <code cpp>#include <iostream> |
+ | using namespace std; | ||
+ | |||
+ | class MyPoint | ||
{ | { | ||
public: | public: | ||
MyPoint(); // constructor implicit | MyPoint(); // constructor implicit | ||
− | MyPoint( double a, double b ); // constructor de initializare | + | MyPoint( const double a ); // constructor de initializare |
− | MyPoint( MyPoint &r ); //constructor de copiere | + | MyPoint( const double a, const double b ); // constructor de initializare |
− | + | MyPoint( const MyPoint &r ); // constructor de copiere | |
− | MyPoint operator+( MyPoint point ); //suma a 2 vectori | + | |
− | MyPoint operator-( MyPoint | + | MyPoint& operator+=( const MyPoint& point ); // adunare cu un alt punct |
− | double operator*( MyPoint point ); //produsul scalar a 2 vectori | + | MyPoint& operator-=( const MyPoint& point ); // scadere |
− | MyPoint | + | MyPoint& operator*=( const double dVal ); // inmultire cu o constanta |
− | + | ||
− | MyPoint | + | // considerand punctul ca un vector de 2 elemente ( pozitiile 0,1 ), pe pozitia 0 fiind x |
− | MyPoint& operator=( | + | // NOTA: nu este recomandat, este doar un exemplu de utilizare al operatorului [] |
− | + | double & operator[]( const int index ); | |
− | MyPoint operator++(); // ++ prefixat | + | |
− | MyPoint operator++( int | + | friend MyPoint operator+ (const MyPoint& p1, const MyPoint& p2); // suma a 2 vectori |
− | + | friend MyPoint operator- (const MyPoint& p1, const MyPoint& p2); // diferenta a 2 vectori | |
− | + | double operator*( const MyPoint& point ) const; // produsul scalar a 2 vectori | |
+ | MyPoint operator*( const double dVal ) const; // multiplicare cu o constanta | ||
+ | friend MyPoint operator*( const double dVal, const MyPoint& point); // pentru a asigura comutativitatea operatiei precedente | ||
+ | |||
+ | MyPoint& operator=( const MyPoint& point ); | ||
+ | |||
+ | MyPoint& operator++(); // ++ prefixat | ||
+ | MyPoint operator++( int ); // ++ postfixat | ||
+ | |||
friend ostream& operator<<( ostream &stream, const MyPoint &pt ); | friend ostream& operator<<( ostream &stream, const MyPoint &pt ); | ||
− | friend istream& operator>>( istream &stream, | + | friend istream& operator>>( istream &stream, MyPoint &pt ); |
− | + | ||
− | + | void setX( double x ); | |
− | + | double getX() const; | |
− | + | ||
− | + | void setY( double y ); | |
+ | double getY() const; | ||
private: | private: | ||
double x; | double x; | ||
double y; | double y; | ||
}; | }; | ||
− | + | ||
MyPoint::MyPoint() | MyPoint::MyPoint() | ||
{ | { | ||
Line 78: | Line 101: | ||
y = 0; | y = 0; | ||
} | } | ||
− | + | ||
− | MyPoint::MyPoint( MyPoint &r ) | + | MyPoint::MyPoint( const MyPoint &r ) |
{ | { | ||
x = r.x; | x = r.x; | ||
y = r.y; | y = r.y; | ||
} | } | ||
− | + | ||
− | MyPoint::MyPoint( double a, double b ) | + | MyPoint::MyPoint( const double a ) |
+ | { | ||
+ | x = a; | ||
+ | y = a; | ||
+ | } | ||
+ | |||
+ | MyPoint::MyPoint( const double a, const double b ) | ||
{ | { | ||
x = a; | x = a; | ||
y = b; | y = b; | ||
} | } | ||
− | + | ||
− | MyPoint MyPoint::operator+( MyPoint | + | MyPoint& MyPoint::operator+=( const MyPoint& point ) |
+ | { | ||
+ | x+=point.x; | ||
+ | y+=point.y; | ||
+ | return *this; | ||
+ | } | ||
+ | |||
+ | MyPoint& MyPoint::operator-=( const MyPoint& point ) | ||
+ | { | ||
+ | x-=point.x; | ||
+ | y-=point.y; | ||
+ | return *this; | ||
+ | } | ||
+ | |||
+ | MyPoint& MyPoint::operator*=( const double dVal ) | ||
+ | { | ||
+ | x*=dVal; | ||
+ | y*=dVal; | ||
+ | return *this; | ||
+ | } | ||
+ | |||
+ | MyPoint operator+ (const MyPoint& p1, const MyPoint& p2) | ||
{ | { | ||
MyPoint temp; | MyPoint temp; | ||
− | temp.x = x + | + | |
− | temp.y = y + | + | temp.x = p1.x + p2.x; |
+ | temp.y = p1.y + p2.y; | ||
+ | |||
return temp; | return temp; | ||
} | } | ||
− | MyPoint | + | MyPoint operator- (const MyPoint& p1, const MyPoint& p2) |
{ | { | ||
MyPoint temp; | MyPoint temp; | ||
− | temp.x = x - | + | |
− | temp.y = y - | + | temp.x = p1.x - p2.x; |
+ | temp.y = p1.y - p2.y; | ||
+ | |||
return temp; | return temp; | ||
} | } | ||
− | double MyPoint::operator*( MyPoint point ) | + | double MyPoint::operator*( const MyPoint& point ) const |
+ | { | ||
+ | // produs vectorial intre 2 vectori | ||
+ | // fiecare vector e determinat de origine si coordonatele punctului | ||
+ | return x * point.x + y * point.y; | ||
+ | } | ||
+ | |||
+ | MyPoint MyPoint::operator*( const double dVal ) const | ||
{ | { | ||
− | return x * | + | return MyPoint(x * dVal, y * dVal); |
} | } | ||
− | + | ||
− | MyPoint | + | MyPoint operator*( const double dVal, const MyPoint& point ) |
{ | { | ||
− | x * | + | return MyPoint(point.x * dVal, point.y * dVal); |
− | |||
− | |||
} | } | ||
− | + | ||
− | MyPoint& MyPoint::operator=( MyPoint &point ) | + | MyPoint& MyPoint::operator=( const MyPoint& point ) |
{ | { | ||
x = point.x; | x = point.x; | ||
Line 125: | Line 184: | ||
return *this; | return *this; | ||
} | } | ||
− | + | ||
− | MyPoint& MyPoint::operator | + | MyPoint& MyPoint::operator++() |
{ | { | ||
− | x | + | x++; |
− | y | + | y++; |
return *this; | return *this; | ||
} | } | ||
− | + | ||
− | MyPoint MyPoint::operator++() | + | MyPoint MyPoint::operator++( int ) |
{ | { | ||
+ | MyPoint r = *this; | ||
x++; | x++; | ||
y++; | y++; | ||
− | return | + | return r; |
} | } | ||
− | + | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
ostream& operator<<( ostream &stream, const MyPoint &pt ) | ostream& operator<<( ostream &stream, const MyPoint &pt ) | ||
{ | { | ||
− | stream << "( " << pt.x << ", " << pt.y << " ) | + | stream << "( " << pt.x << ", " << pt.y << " )"; |
− | return stream; | + | return stream; |
} | } | ||
− | + | ||
− | istream& operator>>( istream &stream, | + | istream& operator>>( istream &stream, MyPoint &pt ) |
{ | { | ||
stream >> pt.x >> pt.y; | stream >> pt.x >> pt.y; | ||
return stream; | return stream; | ||
} | } | ||
− | + | ||
− | + | void MyPoint::setX( double x ) | |
− | + | { | |
+ | this->x = x; | ||
+ | } | ||
+ | |||
+ | double MyPoint::getX() const | ||
{ | { | ||
return x; | return x; | ||
} | } | ||
− | + | ||
− | + | ||
+ | void MyPoint::setY( double y ) | ||
+ | { | ||
+ | this->y = y; | ||
+ | } | ||
+ | |||
+ | double MyPoint::getY() const | ||
{ | { | ||
return y; | return y; | ||
} | } | ||
− | + | ||
− | int main( int argc, char *argv[] ) | + | double& MyPoint::operator[]( const int index ) |
+ | { | ||
+ | if( index == 0 ) return x; | ||
+ | return y; | ||
+ | } | ||
+ | |||
+ | int main( int argc, char *argv[] ) | ||
{ | { | ||
+ | // initializarea punctelor | ||
MyPoint pt( 5, 7 ); | MyPoint pt( 5, 7 ); | ||
− | cout << pt << endl; //afiseaza la consola ( 5, 7 ) | + | MyPoint pt2; |
− | cout << pt++ << endl; //afiseaza la consola ( 5, 7 ) | + | |
− | cout << pt << endl; //afiseaza la consola ( 6, 8 ) | + | // test pentru serializare |
− | cout << ++pt << endl; //afiseaza la consola ( 7, 9 ) | + | cout << pt << endl; // afiseaza la consola ( 5, 7 ) |
+ | cout << pt++ << endl; // afiseaza la consola ( 5, 7 ) | ||
+ | cout << pt << endl; // afiseaza la consola ( 6, 8 ) | ||
+ | cout << ++pt << endl; // afiseaza la consola ( 7, 9 ) | ||
+ | |||
+ | pt = MyPoint( 1, 3 ); | ||
+ | |||
+ | // Multiplicarea cu o constanta. folosirea operatorului * | ||
+ | // Folosim atat operatorul * cat si operatorul * ca si metoda friend. | ||
+ | // 2 * pt este echivalent cu: *(2, pt) | ||
+ | // pt * 2 este echivalent cu: pt.*(2) | ||
+ | cout << 2 * pt << " " << pt * 2 << endl; // afiseaza la consola ( 2, 6 ) (2, 6) | ||
+ | |||
+ | // folosirea operatorului [] supraincarcat. | ||
+ | cout << "x pt[0]=" << pt[0] << "; y pt[1]=" << pt[1] << endl; | ||
+ | pt[0] = 5; | ||
+ | pt[1] = 5; | ||
+ | |||
+ | pt2 = pt; | ||
+ | |||
+ | // Folosirea operatorului + si - supraincarcate si a constructorului | ||
+ | // cu un singur parametru double. Avantajul e ca logica + e definita | ||
+ | // intr-un singur loc. Dezavantajul e chemarea transparenta a constructorului | ||
+ | // si creerea unui obiect temporar. | ||
+ | pt2 = pt2 - 2; // echivalent cu: pt2.=(pt2.-(MyPoint(2))); | ||
+ | cout << pt2 << endl; | ||
+ | |||
+ | pt2 = 2 + pt2; // echivalent cu: pt2.=(MyPoint(2).+(pt2)); | ||
+ | cout << pt2 << endl; | ||
+ | |||
+ | pt2 = pt2 + pt2; // echivalent cu: pt2.=(pt2.+(pt2)); | ||
+ | cout << pt2 << endl; | ||
+ | |||
+ | return 0; | ||
} | } | ||
− | --[[User:Radubolovan|Radu Bolovan]] 12:04, 22 November 2005 (EET) | + | </code> |
+ | |||
+ | ---- | ||
+ | |||
+ | Versiunea originală de [[User:Radubolovan|Radu Bolovan]] 12:04, 22 November 2005 (EET) | ||
+ | |||
+ | [[Category:Tutorial]] | ||
+ | [[Category:Programming]] |
Latest revision as of 12:16, 12 January 2007
Titlul oribil este rezultatul unei limitări in Wiki -- imediat ce se rezolvă, vom face şi titul mai uşor de privit
Introducere
Operatiile de baza sunt implementate in limbajul C++ cu ajutorul operatorilor. In limbajul C++ operatorii sunt, de fapt, functii. Operatorii oferiti de limbaj pot lucra decat asupra tipurilor de date predefinite. Pentru a folosi operatorii asupra tipurilor de date construite de noi, limbajul C++ ne ofera un mecanism care se numeste supraincarcarea operatorilor.
De ce am avea nevoie de asa ceva? Dupa cum se stie se poate crea o functie a unei clase care sa faca exact ce ar face un operator si care sa o definim (de exemplu) suma. Atunci am apela acea metoda astfel:
Obiect ob1; Obiect ob2; Obiect ob3 = ob1.suma( ob2 );
Cam greoi! Ar fi mai simplu daca am scrie aşa:
Obiect ob3 = ob1 + ob2;
Astfel s-a ajuns la nevoia de a supraincarcarea operatorii!
Teorie
Supraincarcarea metodelor claselor in general si a operatorilor in particular sunt doua dintre cele mai importante mecanisme ale limbajului C++. Ele sunt un pas urias catre polimorfism (asta este alta bazaconie inventata de oamenii cu creiere luminate). Aceste oportunitati sunt cele care ofera flexibilitate aplicatiilor pe care le construim.
Iata lista operatorilor din C++ care se pot supraincarca:
- new delete
- () []
- + - * / %
- ^ & | ~
- ! = < >
- += -= *= /= %=
- ^= &= |=
- << >>
- >>= <<=
- == != <= >=
- && ||
- ++ --
- ,
- ->*
- ->
unde operatorul () este apelul unei functii, iar operatorul [] este operatorul de indexare.
Urmatorii operatori nu se pot supraincarca:
- . .* :: ?: sizeof
Reguli care trebuie sa fie respectate in supraincarcarea operatorilor:
- operatorii =, (), [], si -> trebuie sa fie membrii nestatici ai clasei
- operatorul = nu poate fi mostenit
- operatorii pentru tipurile predefinite ale limbajului nu se pot supraincarca
- operatorii nu pot avea argumente implicite
Operatorii pentru un anumit tip definit de utilizator (clasa) pot sa fie sau nu membru al clasei. In cazul in care se supraincarca un operator pentru o clasa, dar acel operator nu apartine clasei, trebuie sa fie declarat friend in clasa respectiva si sa aiba cel putin un argument de tipul clasei respective. Exceptie de la aceasta regula fac operatorii = () [] -> care nu pot fi supraincarcati folosind functii de tipul friend ale unei anumite clase.
Exemplu
Un exemplu: voi da ca exemple supraincarcarea operatorilor ++ pre si post indexat.
#include <iostream>
using namespace std;
class MyPoint
{ public: MyPoint(); // constructor implicit MyPoint( const double a ); // constructor de initializare MyPoint( const double a, const double b ); // constructor de initializare MyPoint( const MyPoint &r ); // constructor de copiere
MyPoint& operator+=( const MyPoint& point ); // adunare cu un alt punct MyPoint& operator-=( const MyPoint& point ); // scadere MyPoint& operator*=( const double dVal ); // inmultire cu o constanta
// considerand punctul ca un vector de 2 elemente ( pozitiile 0,1 ), pe pozitia 0 fiind x // NOTA: nu este recomandat, este doar un exemplu de utilizare al operatorului [] double & operator[]( const int index );
friend MyPoint operator+ (const MyPoint& p1, const MyPoint& p2); // suma a 2 vectori friend MyPoint operator- (const MyPoint& p1, const MyPoint& p2); // diferenta a 2 vectori double operator*( const MyPoint& point ) const; // produsul scalar a 2 vectori MyPoint operator*( const double dVal ) const; // multiplicare cu o constanta friend MyPoint operator*( const double dVal, const MyPoint& point); // pentru a asigura comutativitatea operatiei precedente
MyPoint& operator=( const MyPoint& point );
MyPoint& operator++(); // ++ prefixat MyPoint operator++( int ); // ++ postfixat
friend ostream& operator<<( ostream &stream, const MyPoint &pt ); friend istream& operator>>( istream &stream, MyPoint &pt );
void setX( double x ); double getX() const;
void setY( double y ); double getY() const; private: double x; double y; };
MyPoint::MyPoint() { x = 0; y = 0; }
MyPoint::MyPoint( const MyPoint &r ) { x = r.x; y = r.y; }
MyPoint::MyPoint( const double a ) { x = a; y = a; }
MyPoint::MyPoint( const double a, const double b ) { x = a; y = b; }
MyPoint& MyPoint::operator+=( const MyPoint& point ) { x+=point.x; y+=point.y; return *this; }
MyPoint& MyPoint::operator-=( const MyPoint& point ) { x-=point.x; y-=point.y; return *this; }
MyPoint& MyPoint::operator*=( const double dVal ) { x*=dVal; y*=dVal; return *this; }
MyPoint operator+ (const MyPoint& p1, const MyPoint& p2) { MyPoint temp; temp.x = p1.x + p2.x; temp.y = p1.y + p2.y; return temp; } MyPoint operator- (const MyPoint& p1, const MyPoint& p2) { MyPoint temp; temp.x = p1.x - p2.x; temp.y = p1.y - p2.y; return temp; } double MyPoint::operator*( const MyPoint& point ) const { // produs vectorial intre 2 vectori // fiecare vector e determinat de origine si coordonatele punctului return x * point.x + y * point.y; }
MyPoint MyPoint::operator*( const double dVal ) const { return MyPoint(x * dVal, y * dVal); }
MyPoint operator*( const double dVal, const MyPoint& point ) { return MyPoint(point.x * dVal, point.y * dVal); }
MyPoint& MyPoint::operator=( const MyPoint& point ) { x = point.x; y = point.y; return *this; }
MyPoint& MyPoint::operator++() { x++; y++; return *this; }
MyPoint MyPoint::operator++( int ) { MyPoint r = *this; x++; y++; return r; }
ostream& operator<<( ostream &stream, const MyPoint &pt ) { stream << "( " << pt.x << ", " << pt.y << " )"; return stream; }
istream& operator>>( istream &stream, MyPoint &pt ) { stream >> pt.x >> pt.y; return stream; }
void MyPoint::setX( double x ) { this->x = x; }
double MyPoint::getX() const { return x; }
void MyPoint::setY( double y ) { this->y = y; }
double MyPoint::getY() const { return y; }
double& MyPoint::operator[]( const int index ) { if( index == 0 ) return x; return y; }
int main( int argc, char *argv[] ) { // initializarea punctelor MyPoint pt( 5, 7 ); MyPoint pt2;
// test pentru serializare cout << pt << endl; // afiseaza la consola ( 5, 7 ) cout << pt++ << endl; // afiseaza la consola ( 5, 7 ) cout << pt << endl; // afiseaza la consola ( 6, 8 ) cout << ++pt << endl; // afiseaza la consola ( 7, 9 )
pt = MyPoint( 1, 3 ); // Multiplicarea cu o constanta. folosirea operatorului * // Folosim atat operatorul * cat si operatorul * ca si metoda friend. // 2 * pt este echivalent cu: *(2, pt) // pt * 2 este echivalent cu: pt.*(2) cout << 2 * pt << " " << pt * 2 << endl; // afiseaza la consola ( 2, 6 ) (2, 6)
// folosirea operatorului [] supraincarcat. cout << "x pt[0]=" << pt[0] << "; y pt[1]=" << pt[1] << endl; pt[0] = 5; pt[1] = 5; pt2 = pt; // Folosirea operatorului + si - supraincarcate si a constructorului // cu un singur parametru double. Avantajul e ca logica + e definita // intr-un singur loc. Dezavantajul e chemarea transparenta a constructorului // si creerea unui obiect temporar. pt2 = pt2 - 2; // echivalent cu: pt2.=(pt2.-(MyPoint(2))); cout << pt2 << endl; pt2 = 2 + pt2; // echivalent cu: pt2.=(MyPoint(2).+(pt2)); cout << pt2 << endl;
pt2 = pt2 + pt2; // echivalent cu: pt2.=(pt2.+(pt2)); cout << pt2 << endl;
return 0; }
Versiunea originală de Radu Bolovan 12:04, 22 November 2005 (EET)