![]() |
Índex |
Miguel A. Almarza
Departament d'Informàtica IES Mare de Deu de la Merce |
Operadors que es poden sobrecarregar | |||||||
+ | - | * | / | % | ^ | & | | |
~ | ! | , | = | < | > | <= | >= |
++ | -- | << | >> | == | != | && | || |
+= | -= | *= | /= | %= | ^= | &= | |= |
<<= | >>= | [ ] | ( ) | -> | ->* | new | delete |
Sobrecàrrega d'operadors binaris. |
#include <iostream.h> class Complex { float a,b; public: Complex(){a=b=0;}; Complex(float x, float y) {a=x;b=y;} void Imprimeix(); Complex operator+(Complex z2); }; void Complex::Imprimeix() { cout << a; if (b>0) cout << " + "; cout << b << "i"; } Complex Complex::operator+(Complex z2) { Complex z; z.a = a + z2.a; z.b = b + z2.b; return z; } main() { Complex z1(2,5),z2(4,8), z; z = z1 + z2; z.Imprimeix(); } |
L'operador binari = | |
class Complex { float a,b; public: Complex(){a=b=0;}; Complex(float x, float y) {a=x;b=y;} void PosaA(float x){a=x;} void PosaB(float y){b=y;} float TornaA() {return a;} float TornaB() {return b;} void Imprimeix(); Complex operator+(Complex z2); Complex operator=(Complex z2); }; Complex Complex::operator=(Complex z2) { a = z2.a; b = z2.b; return *this; } main() { clrscr(); Complex z1(2,5), z2; z2 = z1; z2.Imprimeix(); } | Veiem que quan cridem z2 = z1 estem passant z1 per valor a la funció de
l'operador = i l'objecte que crida la funció és l'objecte z2 amb el punter this. Es
a dir que com que l'objecte que treballa és z2 les instruccions b=z2.b; copien les dades de z1 a z2. Ara pensem en una instrucció del tipus z3 = z2 = z1; Aquesta instrucció s'executa de dreta a esquerra. Es a dir que primer s'executa z2 = z1 i després z3 = objecte tornat per la primera crida. Tenim doncs, que com que la crida z2 = z1 torna l'objecte z2, les dades de z2 seran assignades a z3. |
L'operador binari =, assignació dinàmica de memòria | |
#include <iostream.h> #include <string.h> #include <stdlib.h> class String { char * Cadena; int Longitud; public: String(){Cadena = NULL;Longitud=-1;}; String(char * C); String(const String &ObString); ~String() {delete Cadena;}; void Posa(char * Punter); void ImprimeixDades(); char * TornaPunter(){return Cadena;} int TornaLongitud(){return Longitud;}; String operator=(String &ObString); }; String String::operator=(String &ObString) { String ObTemp(ObString); int n = ObString.Longitud; delete Cadena; Cadena = new char [n+1]; if (!Cadena) { cout << "Falta memòria per a String"; exit(1); } strcpy(Cadena,ObString.Cadena); Longitud = n; return ObTemp; } main() { String Tira1; String Tira2("Hola Pedro"); Tira1 = Tira2; Tira1.ImprimeixDades(); } | Igual que en el constructor de còpia fem una declaració per a la
sobrecàrrega de l'operador igual amb una referència a l'objecte
passat a la funció, en aquest cas l'objecte String que està situat a la
dreta de l'igual: String operator=(String &ObString); La funció que es crida amb la instrucció Tira2 = Tira1 és cridat pel objecte Tira2 o el que es trobi a l'esquerra de l'operador =. Així la funció sobrecarregada = actua sobre Tira2 directament i crea la memòria adequada per posar-hi la mateixa cadena de Tira1. Veiem que, com aquesta funció a de tornar les mateixes dades per a una possible nova assignació es crea l'objecte ObTemp que és l'objecte tornat. És necessari fer-ho d'aquesta manera ja que es farà ús del constructor de còpia en el moment de tornar aquest objecte que té assignació dinàmica de memòria. |
L'operador binari +, assignació dinàmica de memòria | |
#include <iostream.h> #include <string.h> #include <stdlib.h> class String { char * Cadena; int Longitud; public: String(){Cadena = NULL;Longitud=-1;}; String(char * C); String(const String &ObString); ~String() {delete Cadena;}; void Posa(char * Punter); void ImprimeixDades(); char * TornaPunter(){return Cadena;} int TornaLongitud(){return Longitud;}; String operator=(String &ObString); String operator+(String &ObString); }; String String::operator+(String &ObString) { String ObTemp; int n = Longitud+ObString.Longitud; ObTemp.Cadena = new char [n+1]; if (!ObTemp.Cadena) { cout << "Falta memòria per a String"; exit(1); } strcpy(ObTemp.Cadena,Cadena); strcat(ObTemp.Cadena,ObString.Cadena); ObTemp.Longitud = n; return ObTemp; } main() { String Tira1; String Tira2("Hola Pedro"); String Tira3("No se si lo conseguiremos"); Tira1 = Tira2+Tira3; Tira1.ImprimeixDades(); } |
Veiem que la declaració de la funció membre operador+ és fa
igual que la declaració de l'operador = String operator+(String &ObString); Per executar aquesta funció es dona una instrucció com la següent: Tira1 = Tira2+Tira3; Per tant l'objecte que crida la funció operador és l'objecte Tira2 i l'objecte Tira3 es passa per referència a l'operador. Es construeix un objecte provisional ObString que conté les dades i aquest objecte és tornat per la funció. Per tant aquesta funció necessita el constructor de còpia, i també el destructor adient, per tornar aquest objecte ObString. Hem de pensar que l'objecte tornat ha d'assignar-se a Tira1 mitjançant l'operador =, per tant la instrucció Tira1 = Tira2+Tira3; necessita que la sobrecàrrega de l'operador = anterior també estigui feta. |
Preincrement i predecrement | ++Ob, --Ob |
Postincrement i postdecrement | Ob++, Ob-- |
L'operador monari increment ++ | |
class Complex { float a,b; public: Complex(){a=b=0;}; Complex(float x, float y) {a=x;b=y;} void PosaA(float x){a=x;} void PosaB(float y){b=y;} float TornaA() {return a;} float TornaB() {return b;} void Imprimeix(); //Sobrecàrrega d'operadors binaris Complex operator+(Complex z2); Complex operator-(Complex z2); Complex operator=(Complex z2); //Sobrecàrrega d'operadors monaris Complex operator++(); //Operador preincrement Complex operator++(int x); //Operador postincrement Complex operator-(); }; Complex Complex::operator++() { a++; b++; return *this; } Complex Complex::operator++(int x) { a++; b++; return *this; } |
Segons veiem tenim dues declaracions per a l'operador increment ++. La
primera és la declaració de la sobrecàrrega per a l'operador preincrement: Complex operator++(); La segona és la declaració per a l'operador postincrement, i ens fixem que aquesta declaració porta un paràmetre (int x), paràmetre mut, del que només fa ús el compilador per distingir aquesta funció de l'altra. Complex operator++(int x) Veiem que la implementació d'aquestes funcions és la mateixa i que incrementa en 1 les propietats de l'objecte que la crida. Recordem que a les instruccions següents l'objecte que crida la funció és l'objecte Z. Complex Z(3,6); Z++; //Crida a Postincrement ++Z; //Crida a PreincrementTambé ens fixem que a la definició de les funcions apareix la instrucció >return *this molt important per a que les instruccions com Z2 = ++Z; puguin executar-se. Amb aquest return aconseguirem que s'incrementi Z i també que el resultat sigui assignat a Z2. |
L'operador monari - | |
Complex Complex::operator-() { a=-a; b=-b; return *this; } | La implementació d'aquest operador canvia el signe de les propietats del
complex que ho crida. Ens fixem que també torna l'adreça de l'objecte per a que la instrucció que fa la crida pugui fer-ne ús. Podem fer ús doncs de la forma següent: Complex Z(2,5); -Z; Z2 = -Z; |
L'operador == i l'operador && | |
class Complex { float a,b; public: Complex(){a=b=0;}; Complex(float x, float y) {a=x;b=y;} void PosaA(float x){a=x;} void PosaB(float y){b=y;} float TornaA() {return a;} float TornaB() {return b;} void Imprimeix(); //Sobrec…rrega d'operadors binaris Complex operator+(Complex z2); Complex operator-(Complex z2); Complex operator=(Complex z2); //Sobrec…rrega d'operadors monaris Complex operator++(); //Operador preincrement Complex operator++(int x); //Operador postincrement Complex operator-(); //Sobrec…rrega d'operadors relacionals i lògics int operator==(Complex z2); int operator&&(Complex z2); }; int Complex::operator==(Complex z2) { return ((a==z2.a)&&(b==z2.b)); } int Complex::operator&&(Complex z2) { return ((a&&z2.a)&&(b&&z2.b)); } main() { clrscr(); Complex z1(2,5), z2(2,5); cout << (z1==z2); } |
z = z1 + z2; z = z1 + 4; z = 4 + z2;Això planteja tres problemes que es resolen a continuació:
L'operador + amb funcions amigues. Classe Complex | |
class Complex { float a,b; public: Complex(){a=b=0;}; Complex(float x, float y) {a=x;b=y;} //Sobrecàrrega d'operadors binaris Complex operator+(Complex z2); Complex operator+(float n); //Sobrecàrrega amb funcions amigues friend Complex operator+(int n, Complex z2); }; | |
Complex Complex::operator+(Complex z2) { Complex z; z.a = a + z2.a; z.b = b + z2.b; return z; } |
A la esquerra tenim l'operador + per tal de fer crides del tipus z=z1+z2 com a funció membre de la classe
complex.
Podíem haver declarat la sobrecàrrega am funcions amigues de la manera següent friend Complex operator+(Complex z1,Complex z2);i implementar aquest operador amb la funció Complex operator+(Complex z1,Complex z2) { Complex z; z.a=z1.a+z2.a; z.b=z1.b+z2.b; return z; }Hem de dir que en aquest cas no és necessari fer ús de les funcions amigues ja que amb una funció membre tenim resolt el problema d'una manera molt més lògica. |
Complex Complex::operator+(float n) { Complex z; z.a = a + n; z.b = b; return z; } | Aquesta sobrecàrrega correspon a una crida com la de la funció main següent:
main() { Complex z1(3,3),z; z = z1 + 4; z.Imprimeix(); }Veiem que la crida z = z1 + 4 es fa mitjançant dos objectes de tipus diferents. Lobjecte que fa la crida, z1, és un Complex i per tant l'operador + serà una funció membre que rep com a paràmetre l'objecte que està a la dreta del signe mas en el moment de la crida, en aquest cas el número 4. |
Complex operator+(int n, Complex z2) { Complex z=z2; z.a += n; return z; } |
Ens falta fer una crida del tipus z = 4 + z1; com a la funció main següent
main() { Complex z1(2,8),Z; z = 4 + z1; z.Imprimeix(); }Tenim doncs que ara no hi ha cap objecte que cridi a la funció més. Aixa vol dir que hem de fer una funció amiga de la classe Complex que rebi els dos paràmetres de tipus float i Complex, i en aquest ordre. Aquesta funció amiga serà la sobrecàrrega de l'operador + de la classe Complex per a aquest cas. |
String Tira; String Tira1("Hola Maria"); String Tira2("Hola Pedro"); Tira = Tira1 + Tira2; Tira = Tira1 + " CARAMBA"; Tira = "CARAMBA " + Tira2;Veiem que la primera concatena dos objectes de la classe String, la segona concatena un String amb un vector de caràcters i la tercera concatena un vector de caràcters amb un objecte de tipus String.
L'operador + amb funcions amigues. Classe String. | |
class String { char * Cadena; int Longitud; public: String(){Cadena = NULL;Longitud=-1;}; String(char * C); String(const String &ObString); ~String() {delete Cadena;}; void Posa(char * Punter); void ImprimeixDades(); char * TornaPunter(){return Cadena;} int TornaLongitud(){return Longitud;}; String operator=(String &ObString); String operator+(String &ObString); String operator+(char * C); friend String operator+(char * C,String &ObString); }; String String::operator+(String &ObString) { String ObTemp; int n = Longitud+ObString.Longitud; ObTemp.Cadena = new char [n+1]; if (!ObTemp.Cadena) { cout << "Falta mem•ria per a String"; exit(1); } strcpy(ObTemp.Cadena,Cadena); strcat(ObTemp.Cadena,ObString.Cadena); ObTemp.Longitud = n; return ObTemp; } String String::operator+(char * C) { String ObTemp; int n = Longitud+strlen(C); ObTemp.Cadena = new char [n+1]; if (!ObTemp.Cadena) { cout << "Falta mem•ria per a String"; exit(1); } strcpy(ObTemp.Cadena,Cadena); strcat(ObTemp.Cadena,C); ObTemp.Longitud = n; return ObTemp; } String operator+(char * C,String &ObString) { String ObTemp; int n = strlen(C)+ObString.Longitud; ObTemp.Cadena = new char [n+1]; if (!ObTemp.Cadena) { cout << "Falta memòria per a String"; exit(1); } strcpy(ObTemp.Cadena,C); strcat(ObTemp.Cadena,ObString.Cadena); ObTemp.Longitud = n; return ObTemp; } |
L'operador [ ] | |
#include <iostream.h> #include <stdlib.h> const int DIMENSIO = 10; class VectorSegur { int v[DIMENSIO]; public: VectorSegur(); int &operator[](int i); }; VectorSegur::VectorSegur() { for (int i=0; i<DIMENSIO; i++) v[i]=i; } int &VectorSegur::operator[](int i) { if ( (i<0) || (i>DIMENSIO-1) ) { cout << "Índex " << i <<" del vector fora de dimensió"; exit(1); } return v[i]; } main() { VectorSegur vDades; cout << vDades[5]; vDades[3]=88; cout << vDades[3]; cout << vDades[25]; } |
Els operadors new i delete | |
#include <iostream.h> #include <stdlib.h> class DadesPersonals { char Nom[45]; char NIF[12]; public: DadesPersonals() {Nom[0]='\0';NIF[0]='\0';} void * operator new(size_t Mida); void operator delete(void * punter){free(punter);} }; void * DadesPersonals::operator new(size_t Mida) { void * pTemporal = malloc(Mida); if (!pTemporal) { cout << "Falta mem•ria per a DadesPersonals"; exit(1); } memset(pTemporal,'a', Mida); return pTemporal; } main() { DadesPersonals * Fitxa1 = new DadesPersonals; //Operador new creat DadesPersonals * Agenda = new DadesPersonals[200]; //Funció new del C++ } |
#include <iostream.h> class Punt { int x,y; public: Punt(int x=0, int y=0) { this->x=x; this->y=y; } operator int() {return x;} }; void Func(int x) { cout << x; } main() { Punt A(5,10); int x, y; x = A; y = x + A; Func(A); } | Veiem la classe Punt que té una nova funció membre operator int()
que és la sobrecàrrega d'aquest operador. En aquest cas estem definint la forma de convertir un punt a enter fent que sigui la abscissa el resultat de la conversió. Així la instrucció x = A de la funció main assigna el valor 5 a x. La instrucció y = x + A converteix A a enter amb valor 5 i ho suma a x. Resultat y val 10. Quan passem el punt A com a paràmetre a una funció que ha de rebre un enter en lloc d'un punt tenim que es fa us de la conversió definida i la crida Func(A) fa que a la funció arribi un enter de valor 5. |
#include <iostream.h> class Punt { int x,y; public: Punt(int x=0, int y=0) { this->x=x; this->y=y; cout << "Executat constructor pel "; cout << "punt: " << x << "," << y << endl; } friend Punt operator+(Punt A,Punt B) { Punt S; S.x = A.x + B.x; S.y = B.y + B.y; return S; } void Imprimeix() { cout << "Punt: " << x << "," << y << endl; } }; main() { Punt A, B(5,7); A = B + 6; A.Imprimeix(); A = 6 + B; B.Imprimeix(); } | Ara a la classe Punt l'hem dotada d'una sobrecàrrega de l'operador + amb una funció amiga.
Si examinem les instruccions de la funció main veiem que s'executa el constructor pels objectes A, B en la primera línia. En la segona instrucció A = B + 6; primer es fa una conversió del numero enter 6 a punt, abans d'executar la suma. Aquesta conversió es fa executant el constructor pel punt 6,0. De forma similar s'executa també el constructor quan fem la instrucció A = 6 + B. Així tenim que en pantalla apareix: ![]() Es clar que també s'executa dues vegades el constructor pel punt S de la funció operator+ ja que la cridem dues vegades. |
#include <iostream.h> #include <stdlib.h> class VectorEnters { int * V; int Longitud; public: VectorEnters(int L); int Posa(int Dada, int i); int Extreu(int i); ~VectorEnters(){delete V;} }; int VectorEnters::Posa(int Dada, int i) { if ((0<=i) && (i<Longitud)) { V[i]=Dada; return 1; } else return 0; } int VectorEnters::Extreu(int i) { if ((0<=i) && (i<Longitud)) return V[i]; else return NULL; } VectorEnters::VectorEnters(int L) { V = new int[L]; if (!V) exit(1); for(int i=0; i<L; i++) V[i]=0; Longitud=L; } |