6.-
SOBRECARGA DE FUNCIONES Y
OPERADORES
C++
proporciona las herramientas necesarias que permiten definir funciones y
operadores que utilizan el mismo nombre o símbolo que una función u operador
incorporado. Estas funciones y operadores se dicen que están sobrecargados y se
pueden utilizar para redefinir una función u operador definido.
6.1
Sobrecarga
de funciones
En
C++ dos o más funciones pueden tener el mismo nombre representando cada una de
ellas un código diferente. Esta característica se conoce como sobrecarga de
funciones.
Una función sobrecargada es una función
que tiene más de una definición.
Estas funciones se diferencian en el número y tipo de argumentos, pero también
hay funciones sobrecargadas que devuelven tipos distintos.
Sobrecarga se refiere al uso de un mismo nombre para múltiples significados de
un operador o una función.
Dado el énfasis del concepto de clase, el uso principal de la sobrecarga es con
las funciones miembro de una clase. Cuando más de una función miembro con
igual nombre se declara en una clase, se dice que el nombre de la función está
sobrecargado en esa clase. El ámbito del nombre sobrecargado es el de la clase.
La sobrecarga de funciones amigas es similar a la de funciones miembro, con la
única diferencia de que es necesaria la palabra reservada friend
al igual que en la declaración de cualquier función amiga.
6.2
Sobrecarga de
operadores
De
modo análogo a la sobrecarga de funciones, la sobrecarga
de operadores permite al programador dar nuevos significados a los símbolos
de los operadores existentes en C++.
C++ permite a los programadores sobrecargar a los operadores para tipos
abstractos de datos.
Operadores que se pueden sobrecargar:
|
+
|
-
|
*
|
/
|
%
|
^
|
&
|
|
|
|
_
|
'
|
=
|
<
|
>
|
<=
|
>=
|
++
|
|
--
|
<<
|
>>
|
==
|
0
|
%%
|
||
|
+=
|
|
-=
|
*=
|
/=
|
%=
|
&=
|
|=
|
<<=
|
>>=
|
|
[
]
|
(
)
|
->
|
->*
|
new
|
delete
|
|
|
Si
un operador tiene formatos unitarios y binarios (tales como + y &) ambos
formatos pueden ser sobrecargados. Un operador unitario tiene un parámetro,
mientras que un operador binario tiene dos. El único operador ternario, ?:,
no se puede sobrecargar.
Existen una serie de operadores que no se
pueden sobrecargar:
La
sobrecarga de operadores en C++ tiene una serie de restricciones que es
necesario tener presente a la hora de manejar operadores sobrecargados:
|
*
Se pueden sobrecargar sólo
los operadores definidos en C++ |
|
*
La sobrecarga de
operadores funciona sólo cuando se aplica a objetos de una clase |
|
*
No se puede cambiar la
preferencia o asociatividad de los operadores en C++ |
|
*
No se puede cambiar un
operador binario para funcionar con un único objeto |
|
*
No se puede cambiar un
operador unitario para funcionar con dos objetos |
|
*
No se puede sobrecargar
un operador que funcione exclusivamente con punteros |
La
clave para la sobrecarga de un operador es una función incorporada a C++ que
permite al programador sustituir una función definida por el usuario para uno
de los operadores existentes.
Para sobrecargar un operador, se debe definir lo que significa la operación
relativa a la clase a la cual se aplica. Para hacer esta operación, se crea una
función operador, que define su acción. El formato general de una función
operador es el siguiente:
tipo nombre_clase::operator op (lista_argumentos) {...}
tipo
es el tipo del valor devuelto por la operación especificada. Los operadores
sobrecargados, con frecuencia tienen un valor de retorno que es del mismo tipo
que la clase para la cual el operador se sobrecarga.
Las funciones operador deben ser miembro o amigas de la clase que se está
utilizando.
Las funciones operador tienen la misma sintaxis que cualquier otra, excepto que
el nombre de la función es operator op, donde op es
el operador que se sobrecarga.
6.3
Declaración
de funciones operador
Las
funciones operador u operadores
sobrecargados pueden ser o no miembros de una clase. De este modo, se pueden
sobrecargar funciones miembro de una clase, así como funciones amigas.
Una función amiga tiene un argumento para un operador unitario y dos para uno
binario. Una función miembro tiene cero argumentos para un operador unitario y
uno para un operador binario.
6.4
Sobrecarga de
operadores unitarios
Consideramos
una clase t y un objeto x de tipo t y definimos un operador unitario
sobrecargado: ++, que se interpreta como:
operator++(x)
o bien como x.operator()
Ejemplo:
|
class
vector |
|
{ |
|
double
x,y; |
|
public: |
|
void
iniciar(double a, double b){x=a; y=b;} |
|
void
visualizar() |
|
{ |
|
cout<<"vector
"<<x<<","<<y<<endl; |
|
} |
|
double
operator++(){x++;y++;} |
|
}; |
|
void
main() |
|
{ |
|
vector v; |
|
v.iniciar(2.5,7.1); |
|
v++; |
|
v.visualizar();
// visualiza 3.5 8.1 |
|
} |
6.5
Versiones
prefija y postfija de los operadores
++ y
--
La
versión prefija del operador de incremento se sobrecarga definiendo una
versión de ++ de un parámetro; la versión
postfija se sobrecarga definiendo una versión de ++
de dos parámetros, siendo el segundo de tipo int
(será un parámetro mudo).
Sobrecargar
un operador unitario como función miembro.
|
class
c |
|
{
int x; |
|
public: |
|
c()
{x=0;} |
|
c(int
a) {x=a;} |
|
c&
operator--() {--x;return *this;} |
|
void
visualizar() {cout<<x<<endl;} |
|
}; |
|
void
main() |
|
{
c ejemplo(6); |
|
--ejemplo;
//ejemplo.operator--(); |
|
} |
La
función --() está declarada; ya que es una función miembro, el sistema pasa
el puntero this implícitamente. Por consiguiente, el objeto que llama a la
función miembro se convierte en el operando de este operador.
Sobrecarga
de un operador unitario como una función amiga.
|
class
c |
|
{
int x; |
|
public: |
|
c()
{x=0;} |
|
c(int
a) {x=a;} |
|
friend
c& operator--(c y) {--y.x;return
y;} |
|
void
visualizar() {cout<<x<<endl;} |
|
}; |
|
void
main() |
|
{
c ejemplo(6); |
|
--ejemplo;
//operator--(ejemplo); |
|
} |
La
función --() está definida; ya que es una función amiga, el sistema no pasa
el puntero this implícitamente. Por consiguiente, se debe pasar explícitamente
el objeto.
6.6
Sobrecarga de
operadores binarios
Los
operadores binarios se pueden sobrecargar pasando a la función dos argumentos.
El primer argumento es el operando izquierdo del operador sobrecargado y el
segundo argumento es el operando derecho. Suponiendo dos objetos x e y de una
clase c, se define un operador binario + sobrecargado. Entonces x + y se puede
interpretar como operator+(x,y) o
como x.operator+(y)
Un
operador binario puede, por consiguiente, ser definido:
- como un amigo de dos argumentos
- como una función miembro de un argumento (caso más frecuente)
- nunca los dos a la vez
Sobrecarga
de un operador binario como función miembro
El
siguiente ejemplo muestra cómo sobrecargar un operador binario como una función
miembro:
|
class
binario |
|
{ |
|
int x; |
|
public: |
|
binario()
{x=0;} |
|
binario(int a)
{x=a;} |
|
binario operator + (binario
&); |
|
void visualizar() {cout<<x<<endl; |
|
}; |
|
binario
binario::operator +(binario &a) |
|
{ |
|
binario aux; |
|
aux.x=x+a.x; |
|
return aux; |
|
} |
|
void
main() |
|
{ |
|
binario
primero(2),segundo(4),tercero; |
|
tercero = primero + segundo; |
|
tercero.visualizar(); |
|
} |
|
La
salida del programas es 6. |
Sobrecarga
de un operador binario como una función amiga
|
class
binario |
|
{ |
|
int x; |
|
public: |
|
binario()
{x=0;} |
|
binario(int a)
{x=a;} |
|
friend binario operator + (binario &,binario &); |
|
void visualizar() {cout<<x<<endl; |
|
}; |
|
binario
binario::operator +(binario &a,binario &b) |
|
{
binario aux; |
|
aux.x=a.x+b.x; |
|
return aux; } |
|
void
main() |
|
{
binario
primero(2),segundo(4),tercero; |
|
tercero = primero
+ segundo; |
|
tercero.visualizar(); |
|
} |
|
La
salida del programa será 6. |
La
función operador binario +() está declarada; debido a que es una función
amiga, el sistema no pasa el puntero this implícitamente y, por consiguiente,
se debe pasar el objeto binario explícitamente con ambos argumentos. Como
consecuencia, el primer argumento de la función miembro se convierte en el
operando izquierdo de este operador y el segundo argumento se pasa como operando
derecho.
6.7
Sobrecargando
el operador de llamada a funciones (
)
Uno
de los operadores más usuales es el operador de llamada a función y puede ser
sobrecargado. La llamada a función se considera como un operador binario
expresión
principal (lista de expresiones)
Donde
expresión principal es un operando y lista de expresiones (que
puede ser vacía) es el otro operando.
La función operador correspondiente es operator()
y puede ser definida por el usuario para una clase c sólo mediante una función
miembro no estática.
x(i) equivale a
x.operator() (i)
x(i,j) equivale a
x.operator()(x,y)
6.8
Sobrecargando
el operador subíndice [
]
El
operador [] se utiliza normalmente
como índice de arrays. En realidad este operador realiza una función útil;
ocultar la aritmética de punteros. Por ejemplo, si se tiene el siguiente array:
char
nombre[30]; y
se ejecuta una sentencia tal como car
= nombre[15]; el
operador [] dirige la sentencia de asignación para sumar 15 a la dirección
base del array nombre para localizar los datos almacenados en esta posición de
memoria.
En C++ se puede sobrecargar este operador y proporciona muchas extensiones útiles
al concepto de subíndices de arrays. [] se considera un operador binario porque
tiene dos argumentos. En el ejemplo p=x[i]
Los
argumentos son x e i. La función operador correspondiente es operator []; ésta
puede ser definida para una clase x sólo mediante un función miembro. La
expresión x[i] donde x es un objeto de una determinada clase, se interpreta
como x.operator[](y).
6.9
Sobrecarga de
operadores de flujo
Las
sentencias de flujos se utilizan para entradas y salidas. Los flujos no son
parte de C++, pero se implementan como clases en la biblioteca de C++. Las
declaraciones para estas clases se almacenan en el archivo de cabecera
iostream.h. En C++ es posible sobrecargar los operadores de flujo de entrada y
salida de modo que pueda manipular cualquier sentencia de flujo que incluya
cualquier tipo de clase. Como se definen en el archivo de cabecera iostream.h,
estos operadores trabajan con todos los tipos predefinidos, tales como int,
long, double,char. Sobrecargando estos los operadores de flujo de entrada y
salida, estos pueden además manipular cualquier tipo de objetos de clases.
Ejemplo
de sobrecarga de los flujos << y >>
|
class
punto
|
|
{
|
|
int x,y;
|
|
public:
|
|
punto()
{x=y=0;}
|
|
punto (int xx,int yy)
{ x=xx;y=yy;}
|
|
void fijarx(int xx)
{ x=xx;}
|
|
void fijary(int yy)
{y=yy;}
|
|
int leerx()
{return x;}
|
|
int leery()
{return y;}
|
|
friend ostream& operator <<
(ostream& os,const punto &p);
|
|
friend istream& operator >>
(istream& is,const punto &p);
|
|
};
|
|
void
main()
|
|
{
|
|
punto p;
|
|
cout<<p<<endl;
|
|
p.fijarx(50);
|
|
p.fijary(100);
|
|
cout<<p<<endl;
|
|
cout
<<"introducir los valores de x e y\n";
|
|
cin>>p;
|
|
cout<<"se ha
introducido"<<p;
|
|
}
|
|
ostream&
operator<<(ostream &os,punto &p)
|
|
{
|
|
os <<"x= "<<p.x<<",y=
"<<p.y;
|
|
return os;
|
|
}
|
|
istream&
operator>>(istream &is,punto &p)
|
|
{
|
|
is >>p.x>>p.y;
|
|
return
is;
|
|
}
|
Es
posible poner en cascada múltiples objetos en una sentencia de flujo de salida:
punto
p1,p2,p3;
cout<<p1<<":"<<p2<<":"<<p3;
|