![]() |
|---|
![]() |
![]() |
![]() |
Operadores
Desde que estudiamos C vimos la capacidad que ten�a este lenguaje de que una funci�n se pudiese llamar igual que otra, siempre que los par�metros que se le pasen o que ella devuelva sean diferentes a su hom�nimos.
En C++ esta propiedad es completamente valida.
Un caso particular pero en extremo �til de la sobrecarga de funciones es la redefinici�n de los operadores.
operadores
Los operadores son funciones especializadas que devuelven valores. As� cuando escribimos: A + B, entendemos que + es una funci�n del objeto A, a la que se le pasa como par�metro la variable B y devuelve un valor, en general, del tipo de A y B. En los dos formatos de escritura de las funciones que hemos visto tendr�amos:
A.+(B) //cuando A y B han sido declarados directamente como objetos.
A ->+(B) //cuando A fue declarado como puntero.
As� visto, los operadores son funciones que trae definidas C++ para sus clases y ahora nosotros podemos redefinirlos para nuestras clases de manera que puedan cumplir en nuestros programas el mismo rol que estamos acostumbrados a verlos desempe�ar.
Tomemos la clase Resistor
| class Resistor { private : float res; public: Resistor (float resp=0.0) { res = resp; } void MuestraR() { printf("El valor de su resistencia es: %f",res);} }; |
Si ahora declaramos un objeto R1 y otro R2 de tipo Resistor, ser�a de inter�s nuestro poder hacer:
R1 = R1 + R2;
De esta forma nuestros objetos realizan en nuestros programas las mismas operaciones que tienen en su funcionamiento real.
Para realizar esta sobrecarga de la funci�n + utilizaremos la palabra reservada de C++ operador.
La sintaxis de este tipo de funci�n es:
| <Tipo de dato que devuelve> | <Palabra reservada "operador"> | <Signo del operador> | <(Tipo del parametro que se le pasa a la funci�n)> |
La l�nea donde redefinimos el operador "+()" para nuestra clase Resistor quedar�a:
| Resistor operador +(Resistor R1) ; |
y el cuerpo de la funci�n:
| Resistor Resistor::operador + (Resistor R1) {Resistor
R0(); |
Desglosemos tanto la definici�n como el cuerpo de la funci�n.
En la primera aparece el tipo de dato que va a devolver la funci�n, en este caso, "Resistor", a continuaci�n la palabra reservada "operador" y el signo "+()" que es en este caso la funci�n que vamos a sobrecargar, por �ltimo entre par�ntesis esta el tipo de dato que le vamos a pasar a la funci�n y que vuelve a ser "Resistor" y el nombre del par�metro "R1".
Mientras en el cuerpo de la funci�n tenemos:
Declaramos un objeto local "R0" de tipo Resistor. No le pasamos ning�n par�metro ya que en la clase se utiliz� como par�metro impl�cito "0".
Como lo que queremos hacer es sumar el valor de cada resistencia tomamos tanto el valor del dato "res" del par�metro "R1" como el del propio objeto que hace el llamado a la funci�n.
Detengamonos aqu�. La funci�n "+()" como toda funci�n de una clase responde al objeto que la invoca, as� vemos que en el primer sumando podemos poner solamente "res" ya que al ser la funci�n del propio objeto tiene derecho a utilizar los datos internos. No ocurre lo mismo con el dato "res" correspondiente al par�metro que se le pasa a la funci�n, en este caso "R1", y por ello en el segundo sumando ponemos "R1.res".
Ambos valores son de tipo float de manera que podemos usar el operador "+()" como viene definido por C++. El resultado de esa suma ser� por ende otro float y puede ser asignado tambi�n normalmente por "=" al dato "R0.res" del objeto que definimos de forma local en la funci�n que estamos construyendo.
Como quiera que la funci�n debe devolver un dato de tipo Resistor terminamos la funci�n con return(R0).
Nos falta por instrumentar el operador "=" ya que el resultado de la funci�n que hemos invocado es un nuevo objeto de tipo Resistor, que debe ser asignado al objeto R3.
La definici�n de la funci�n "=()" ser� entonces:
| void operador =(Resistor RR); |
y el cuerpo de la funci�n:
| void Resistor::operador = (Resistor RR) { this->res = RR.res; } |
En este caso hemos definido la funci�n "=()" sin devolver ning�n tipo de dato y utilizamos la palabra reservada this que nos referencia el propio objeto que esta haciendo la llamada a la funci�n. Por esto this -> res pone en el dato R3.res el valor que le estamos pasando, que en este caso es R0.res.
Cuando conectamos dos resistencias en paralelo, el resultado es el inverso de la suma de los inversos de sus valores nominales, de manera que si queremos continuar incorporando el comportamiento que tienen las resistencias a nuestra clase ser�a �til tener una funci�n que realice esa operaci�n. Como ya usamos el signo "+" para la conecci�n en serie ahora podemos usar el signo "/" para la conecci�n en paralelo:
| void operador /(Resistor RR) ; |
y el cuerpo de la funci�n:
| Resistor Resistor::operador / (Resistor R0) { Resistor R0(); R0.res = res*R1.res/(res + R1.res); return (R0); } |
Si a�adimos esas tres funciones a la clase que ya teniamos de Resistor tendremos objetos que reproducen en la programaci�n las acciones que en la realidad realizan este tipo de componentes.
En el siguiente programa se realiza la suma de dos objetos de tipo Resistor utilizando para ello la biblioteca Resistor.h.
| ~ ~
~ ~ ~ Resistor
*R1,*R2,*R3,*R4; |
Al dise�ar una clase muchas veces necesitamos no s�lo sumar objetos del propio
tipo que se esta dise�ando, sino que hay que interactuar con otras clases. Ser�
necesario entonces programar varias veces la propia funci�n y hacer uso de que C++
identifica las funciones no solo por su nombres sino tambien por los par�metros que se le
pasan y los tipos de datos que devuelve.
Un buen ejemplo es el dise�o de una clase de matrices.
| class MatrizI { ~ ~ ~ ~ ~ ~ MatrizI operator +(MatrizI M); MatrizI operator +(int Nr); void operator =(MatrizI M); }; ~ ~ ~ ~ ~ MatrizI MatrizI::operator +(MatrizI M) { MatrizI Temp(M.NrCol,M.NrFila); int Col,Fila; for (Fila=0;Fila<NrFila;Fila++) {for (Col=0;Col<NrCol;Col++) {Temp.LlenaNodo(M.MiValor(Col,Fila)+MiValor(Col,Fila),Col,Fila); } } return (Temp); } MatrizI MatrizI::operator +(int Nr) { MatrizI Temp(NrCol,NrFila); int Col,Fila; for (Fila=0;Fila<NrFila;Fila++) {for (Col=0;Col<NrCol;Col++) {Temp.LlenaNodo(MiValor(Col,Fila)+Nr,Col,Fila); } } return (Temp); } ~ ~ ~ ~ ~ |
Aparecen aqu� dos funciones �+� pero en una le pasamos como parametro una
variable tipo Matriz y en otra uno tipo int.
En dependencia del par�metro que se le pase a la funci�n el compilador escoger� cual
de las dos debe utilizar.
Tanto en Resistor como en MatrizI hemos incorporado a la clase el operador
"=()" aunque siempre que la operaci�n se realice entre dos objetos de
una misma clase esto no es necesario, pues C++ tiene definida la asignaci�n entre objetos
de igual clase con independencia del tipo de objeto.
Funciones "friend".
Nos falta por estudiar el caso inverso al aqu� visto, esto es la suma de un entero y
un objeto tipo MatrizI. Debe notar que en ese caso tenemos que redise�ar no
un operador para una clase que estamos haciendo nosotros sino que hay que cambiar
la forma que actua un operador de una clase ya definida e incluso, como en este
caso, definida originalmente por C++.
Para estos casos C++ nos brinda el tipo de funci�n friend. Esta funci�n puede
acceder a los datos de la clase de la que es friend, en este caso la estamos
haciendo friend de la clase de n�meros enteros.
La sintaxis de este tipo de funci�n aparece en la instrumentaci�n de la clase
"MatrizI_A.h".
| class MatrizI_a { ~ ~ ~ ~ ~ friend MatrizI_A operator +(int Nr,MatrizI_A M); ~ ~ ~ ~ ~ }; MatrizI_A operator +(int Nr, MatrizI_A M) ~ ~ ~ ~ ~ |
Observe que al hacer referencia a la funci�n "+" no se pone:
MatrizI_A MatrizI_A::
Ya que ella no es una funci�n de MatrizI_A sino que es friend de la clase
de los enteros. Observe tambi�n que tiene los dos par�metros entre cuyos tipos se va
definir el nuevo operador. El resto de la instrumentaci�n es igual a la ya vista de suma
de una matriz con un entero.
![]() |
![]() |