Programación en C++/Sobrecarga de Operadores

De Wikilibros, la colección de libros de texto de contenido libre.

Editores:

  1. Oscar E. Palacios
Objetos y Clases Herencia

Tabla de contenidos

[editar] Sobrecarga de operadores

La sobrecarga de operadores es uno de los mecanismos que nos permite ampliar las capacidas de los lenguajes de programación orientados al objeto. En C++, la declaración y definición de una sobrecarga de operador es muy similar a la declaración y definición de una función cualquiera. El ejemplo más sencillo de una sobrecarga de operadores nos lo da el lenguaje mismo, es decir, en una operación aritmética (por ejemplo, una suma ) el compilador determina el tipo de operación requerida de acuerdo con el tipo de datos involucrados. Vamos a suponer que se tienen las variables: int A, B; double X, Y; int R; y las siguientes instrucciones:

R = A + B;
R = A + X;
R = X + Y;

Ahora bien:

  • en el primer caso el compilador determina que el tipo de operación requerida es una suma de enteros debido a que los dos miembros ( A y B ) de la suma son del tipo entero.
  • en el segundo caso parece ser que las cosas no son tan claras, ya que en este los miembros involucrados en la suma son de tipos diferentes, sin embargo el compilador determinará el tipo de operación requerida y depositará en el resultado ( R )el valor resultante redondeado.
  • en el tercero y último de los casos el compilador determina que el tipo de operación requerida es una suma de reales debido a que los dos miembros ( X e Y ) de la suma son del tipo double. También en este caso el resultado de la suma se redondea antes de ponerlo en R.

[editar] Mi primer sobrecarga

Para poner un ejemplo práctico de sobrecarga del operador de suma ( + ) vamos a considerar el caso de la clase pareja mostrada en seguida:

class Pareja {
public:
    double a, b;
 
    // constructor parametrizado
    Pareja(double a, double b)
    {
        this->a = a;
        this->b = b;
    }
};
 
int main()
{
    Pareja A(50, 75 );
    Pareja B(150, 175 );
    Pareja C = A + B;
 
    return 0;
}

Si usted trata de compilar el programa anterior descubrirá que el compilador se queja, es decir, el compilador no sabe que hacer en el caso de la instrucción Pareja C = A + B; aunque es evidente que se trata de la declaracion del objeto C y que el mismo se espera que sea igual a la suma de los objetos A y B. El error se debe al hecho de que el tipo Pareja es en realidad una clase y no un tipo primitivo y, en consecuencia, se debe de adiestrar al mismo compilador para que éste sepa de que manera hara la suma de dos objetos del tipo Pareja. Así, para solucionar el error vamos a sobrecargar el operador + para trabajar la suma de dos de dichos objetos. Vamos:

// Titulo..: programa sobrecarga01.cpp
// Objetivo: demostración de sobrecarga de operadores
// Autor...: Oscar E. Palacios
#include <iostream>
uses namespace std;
 
class Pareja {
public:
    double a, b;
 
    // constructor parametrizado
    Pareja(double a, double b)
    {
        this->a = a;
        this->b = b;
    }
};
 
// Sobrecarga del operador +
pareja& operator +(pareja &p1, pareja &p2)
{
  return *(new pareja(p1.a + p2.a, p1.b + p2.b) );
}
 
int main()
{
    Pareja A(50, 75 );
    Pareja B(150, 175 );
    Pareja C = A + B;
 
    cout << "A = " << A.a << ',' << A.b << "\n";
    cout << "B = " << B.a << ',' << B.b << "\n";
    cout << "C = " << C.a << ',' << C.b << "\n";
 
    return 0;
}

[editar] Sintaxis general

Tal y como usted lo habrá notado, la declaración de sobrecarga en el programa anterior es lo más parecido a una función, es decir, la sintaxis general para sobrecargar uno operador cualquiera es:

tipo  operator # (lista de parámetros);

en donde,

  1. tipo se refiere al tipo regresado por el operador
  2. operator es una palabra reservada y debe aparecer en toda declaración de sobrecarga de operadores
  3. el simbolo # está en representación de cualquier operador
  4. lista de parámetros indica los argumentos sobre los que operarará la función de sobrecarga

Nota: en el caso de que el operador sobrecargado se para una clase específica la sintaxis se extiende de la siguiente manera:

tipo  nombre_clase::operator # (lista de parámetros);

Regresando al caso de la clase Pareja, específicamente al operador + de sobrecarga para dicha clase, nos damos cuenta que dicho operador opera sobre dos objetos de dicha clase y también devuelve un resultado de la misma clase. Esto es lo más recomendable ya que si usted recuerda el compilador regresa un resultado entero si es que (por ejemplo) se le da una instrucción como: R = 100 + 23;. En todo caso, el resultado devuelto por los operadores sobrecargados es responsabilidad del programador.

[editar] Sobrecarga permitida de operadores

En C++ no es posible sobrecargar todos los operadores, pero al menos la mayoría de ellos sí. Los operadores que no se pueden sobrecargar son: #, ##, ., :, .*, o ?.

[editar] Sobrecarga del operador << ( ostream )

Normalmente cuando se escribe una clase y se desea que el stream estándar de salida ( cout ) pueda mostrar una representación de su valor se debe de sobrecargar el operador <<. Para mostrar un ejemplo retomemos el programa sobrecarga01.cpp ( visto arriba ), en el mismo se imprimen por medio de cout los miembros ( a y b ) de los objetos A, B y C.

cout << "A = " << A.a << ',' << A.b << "\n";
cout << "B = " << B.a << ',' << B.b << "\n";
cout << "C = " << C.a << ',' << C.b << "\n";

Usted puede ver cómo para cada uno de los miembros de los objetos se debe de usar el operador de dirección ( . ), pues bien, nuestro objetivo es lograr que dado un objeto de la clase Pareja éste pueda ser desplegado por cout y para ello haremos la sobrecarga de operador << con el fin de que pueda operar con objetos de la clase mencionada. Veamos:

// Titulo..: programa sobrecarga03.cpp
// Objetivo: demostración de sobrecarga de operadores
// Autor...: Oscar E. Palacios
#include <iostream>
uses namespace std;
 
class Pareja {
public:
    double a, b;
 
    // constructor parametrizado
    Pareja(double a, double b)
    {
        this->a = a;
        this->b = b;
    }
};
 
// Sobrecarga del operador +
pareja& operator +(pareja &p1, pareja &p2)
{
  return *(new pareja(p1.a + p2.a, p1.b + p2.b) );
}
 
// Sobrecarga del operador << para la clase Pareja
ostream& operator << (ostream &o, pareja &p)
{
    o << "(" << p.a << ", " << p.b << ")";
    return o;
}
 
int main()
{
    Pareja A(50, 75 );
    Pareja B(150, 175 );
    Pareja C = A + B;
 
    cout << "A = " << A << "\n";
    cout << "B = " << B << "\n";
    cout << "C = " << C << "\n";
 
    return 0;
}

[editar] Sobrecarga del operador >> ( istream )

Así como el operador << debe ser sobrecargado, lo mismo es cierto para el operador >> para poder ser usado con el stream estándar de entrada ( cin ). Retomaremos nuevamente a la clase Pareja para dar un ejemplo.

// Titulo..: programa sobrecarga03.cpp
// Objetivo: demostración de sobrecarga de operadores
// Autor...: Oscar E. Palacios
#include <iostream>
uses namespace std;
 
// inserte aquí la clase Pareja y los operadores sobrecargados vistos
// en el programa anterior.
 
 
// Sobrecarga del operador >> para la clase Pareja
istream& operator >> (istream &i, pareja &p)
{
    cout << "Introducir valores para ( a, b) : ";
    i >> p.a >> p.b;
    cin.ignore();
    return i;
}
 
int main()
{
    Pareja A(50, 75 );
    Pareja B;
    cin >> B;
    Pareja C = A + B;
 
    cout << "A = " << A << "\n";
    cout << "B = " << B << "\n";
    cout << "C = " << C << "\n";
 
    return 0;
}

[editar] Operadores amigos ( friend )

Un operador, amigo al igual que una función amiga, es aquel que aún cuando no es miembro de una clase tiene todos los privilegios de acceso a los miembros de dicha clase. En la sobrecarga de los operadores +, << y >> para la clase Pareja de los programas anteriores se puede notar que dichos operadores no son parte de la clase Pareja, sino más bien estos operan sobre objetos de dicha clase y es a travez de dichos objetos que pueden manipular a los miembros de la clase. Ahora bien, no hemos tenido ningún problema debido a que todos los miembros de Pareja han sido declarados como públicos (public:), pero que sucede si un operador o una función que no sea parte de la clase trata de acceder a los miembros privados o protegidos de ésta, en estos casos el compilador reportaría el error indicando que tal o cúal miembro es privado dentro de cierto contexto. Luego, si queremos que un operador o función que no es miembro de una clase pueda acceder a los miembros publicos, privados o protegidos deberemos declarar a dicho operador o función como amigo (friend) dentro de la clase específica. Para mostrar un ejemplo modificaremos la clase Pareja, en donde sus atributos serán privados.

// Titulo..: programa sobrecarga04.cpp
// Objetivo: demostración de sobrecarga de operadores
// Autor...: Oscar E. Palacios
#include <iostream>
uses namespace std;
 
class Pareja {
 
private:
    double a, b;
 
public: 
    // constructor parametrizado
    Pareja(double a, double b)
    {
        this->a = a;
        this->b = b;
    }
 
    friend pareja& operator +(pareja &p1, pareja &p2);
    friend ostream& operator << (ostream &o, pareja &p);
};
 
// Sobrecarga del operador + para la clase Pareja
pareja& operator +(pareja &p1, pareja &p2)
{
  return *(new pareja(p1.a + p2.a, p1.b + p2.b) );
}
 
// Sobrecarga del operador << para la clase Pareja
ostream& operator << (ostream &o, pareja &p)
{
    o << "(" << p.a << ", " << p.b << ")";
    return o;
}
 
int main()
{
    Pareja A(50, 75 );
    Pareja B(150, 175 );
    Pareja C = A + B;
 
    cout << "A = " << A << "\n";
    cout << "B = " << B << "\n";
    cout << "C = " << C << "\n";
 
    return 0;
}

[editar] Sobrecarga de operadores dentro de una clase

Tal y como hemos dicho antes, los operadores que hemos sobrecargado para la clase Pareja ( de los ejemplos anteriores ) no son parte de la clase, pero en la mayoría de las veces se verá que los operadores para una clase específica se deben sobrecargar dentro de la misma clase, es decir, dichos operadores serán miembros de la clase. Antes de sobrecargar cualquier operador para una clase se deben tener en cuenta los siguientes factores:

  1. Los operadores binarios se declaran con un solo parámetro, ya que el primer parámetro es pasado por el programa como this, es decir, un puntero al mismo objeto.
  2. Los operadores unarios se declaran sin paramétros, ya que el único parámetro es pasado por el programa como this.
Nota:
Los operadores binarios son aquellos que poseen dos partes ( izquierda y derecha),
por ejemplo, una operación de suma requiere dos operandos ( o1 + o2 ).

Los operadores unarios son aquellos que poseen solo una parte, por ejemplo, una operación
de incremento ( o1 ++ ).

Con el propósito de ver un ejemplo práctico vamos a retomar una vez más la tan famosa clase Pareja, salvo que en esta ocasión vamos a sobrecargar dentro de la misma a los operadores binarios: + (suma), - (resta), * (multipliación) y / (division); el operador de asignación (=); el operador de incremento (++) y el operador de comparación (==).

// Programa: sobrecarga05.cpp
// Objetivo: mostrar sobrecarga de operadores
// Autor...: Oscar Edmundo Palacios.
#include <iostream>
 
using namespace std;
 
class Pareja {
 
private:
    int a, b;
 
public:
    // constructor base
    Pareja() : a(0), b(0) {}
 
    // constructor parametrizado
    Pareja(int a, int b) {
        this->a = a;
        this->b = b;
    }
 
    // operadores miembros
    Pareja& operator + (Pareja &p);
    Pareja& operator - (Pareja &p);
    Pareja& operator * (Pareja &p);
    Pareja& operator / (Pareja &p);
    Pareja& operator = (Pareja &p);
    Pareja& operator ++();
    bool    operator ==(Pareja &p);
 
    // operadores no miembros
    friend ostream& operator << (ostream &o, Pareja &p);
    friend istream& operator >> (istream &o, Pareja &p);
};
 
// implementacion de los operadores para la clase Pareja
//....................................
Pareja& Pareja::operator + (Pareja &p)
{
    this->a += p.a;
    this->b += p.b;
    return *this;
}
//....................................
Pareja& Pareja::operator - (Pareja &p)
{
    this->a -= p.a;
    this->b -= p.b;
    return *this;
}
//....................................
Pareja& Pareja::operator * (Pareja &p)
{
    this->a *= p.a;
    this->b *= p.b;
    return *this;
}
//....................................
Pareja& Pareja::operator / (Pareja &p)
{
    if (p.a != 0) this->a /= p.a;
    if (p.b != 0) this->b /= p.b;
    return *this;
}
//....................................
Pareja& Pareja::operator = (Pareja &p)
{
    if (p.a != 0) this->a = p.a;
    if (p.b != 0) this->b = p.b;
    return *this;
}
 
//....................................
Pareja& Pareja::operator ++ ()
{
    this->a ++;
    this->b ++;
    return *this;
}
 
//....................................
bool Pareja::operator == (Pareja &p)
{
    return this->a == p.a && this->b == p.b;
}
 
// implemetaci¢n de operadores no miembros
ostream& operator << (ostream &o, Pareja &p)
{
    o << "(" << p.a << ", " << p.b << ")";
    return o;
}
 
istream& operator >> (istream &i, Pareja &p)
{
    cout << "Introducir valores para ( a, b) :";
    i >> p.a >> p.b;
    cin.ignore();
    return i;
}
 
 
// prueba para la clase Pareja
int main()
{
    Pareja A(50,  75);
    Pareja B(100, 25);
    Pareja C;
 
    cout << "A = " << A << "\n";
    cout << "B = " << B << "\n";
    cout << "........................." << endl;
    C = A * B;
    cout << "A = " << A << "\n";
    cout << "C = " << C << endl;
    cout << "........................." << endl;
 
    ++C;
    cout << "C = " << C << endl;
    cout << "A == B " << ( (A==B) ? "Si": "No" );
    cin.get();
    return 0;
}
Objetos y Clases Arriba Herencia
Herramientas personales