Disponible la nueva versión "donationware" 7.3 de OrganiZATOR
Descubre un nuevo concepto en el manejo de la información.
La mejor ayuda para sobrevivir en la moderna jungla de datos la tienes aquí.

Curso C++

[Home]  [Inicio]  [Índice]


4.9.18b  Sobrecarga de operadores binarios

§1  Sinopsis

Los operadores binarios pueden ser sobrecargados de dos formas:

a.  Declarando una función miembro no estática que acepte un argumento

b.  Declarando una función no miembro (generalmente friend) que acepte dos argumentos.

Según lo anterior, y dependiendo de la declaración, si @ representa el operador binario, la expresión x @ y  entre miembros de una clase C puede ser interpretada como cualquiera de las dos formas:

ax.operator@(y)

boperator@(x, y)

Si han sido declaradas ambas formas, se aplica la congruencia estándar de argumentos ( 4.4.1a) para resolver cualquier posible ambigüedad.

Nota: recuerde la versión a parece recibir solo un argumento, pero en realidad recibe dos si se considera el argumento implícito this ( 4.11.6), de forma que podría considerarse x.operator@(C* this, y). Siendo: C* this = &x [2].

§2  Sobrecarga del operador suma ( + )

En el ejemplo que sigue utilizamos la clase Vector, ya utilizada en ejemplos anteriores ( 4.9.18a) y sobrecargamos el operador suma binaria (+), de forma que pueda ser utilizada con tipos de dicha clase. La técnica es la anteriormente utilizada para el operador de asignación:

#include <iostream>
using namespace std;

class Vector {
  public:
  float x, y;
  Vector operator+ (Vector v) {  // función-operador operator+
    x = x + v.x;
    y = y + v.y;
    return *this;
  }
};

int main () {     // =========
   float x = 5, y = 6;
   cout << "R = " << x + y << endl;  // M.2: versión global
   Vector v1 = {1, 2}, v2 = {3, 4};
   Vector v3 = v1 + v2;              // M.4: versión sobrecargada
   cout << "Rx = " << v3.x << endl;
   cout << "Ry = " << v3.y << endl;
}

Salida:

R = 11
Rx = 4
Ry = 6

Comentario

El ejemplo compila sin dificultad,  confirmando que el operador + puede ser utilizado en M.4 con los objetos v1 y v2 de la clase Vector. El resultado obtenido para v3 es el esperado. Sin embargo, a pesar de su aparente idoneidad, tampoco en este caso el operador ha sido sobrecargado correctamente ( 4.9.18a). Si sustituimos las referencias explícitas a v3 (en las sentencias M.4 y siguientes) por dos resultados auxiliares en la sentencia de salida:

   cout << "Rx = " << (v1 + v2).x << endl;    // M.4:
   cout << "Ry = " << (v1 + v2).y << endl;    // M.5:

El programa suministra la desconcertante salida siguiente:

R = 11
Rx = 4
Ry = 10


La razón de que el valor obtenido para Ry no sea el esperado, estriba en que con el diseño actual, la función operator+ modifica el primer operando, exactamente como lo hacía la función operator= [1]. Esta asignación oculta también podría ser manifestada añadiendo una línea a la versión inicial del programa:

cout << "v1.x = " << v1.x << " v1.y = " << v1.y << endl;    // M.7:

En este caso las salidas habrían sido:

R = 11
Rx = 4
Ry = 6
v1.x = 4 v1.y = 6


§2.1  Para evitar este efecto lateral indeseado de operator+, modificamos su diseño, de forma que no altere ninguno de los operandos involucrados. Para ello utilizamos un objeto nuevo al que aplicamos el resultado:

#include <iostream>
using namespace std;

class Vector {
  public:
  float x, y;
  Vector operator+ (const Vector& v) { // función-operador operator+
    Vector vr;     // objeto temporal
    vr.x = x + v.x;
    vr.y = y + v.y;
    return vr;
  };
};

int main () {      // ===============
   float x = 5, y = 6;
   cout << "R = " << x + y << endl;
   Vector v1 = {1, 2}, v2 = {3, 4};
   Vector v3 = v1 + v2;
   cout << "Rx = " << v3.x << endl;
   cout << "Ry = " << v3.y << endl;
   cout << "v1.x = " << v1.x << " v1.y = " << v1.y << endl;
}

Salida:

R = 11
Rx = 4
Ry = 6
v1.x = 1 v1.y = 2

Comentario

La última salida nos confirma que el diseño utilizado es correcto. Proporciona los resultados apetecidos además de mantener inalterados los valores del primer operando.

Observe que la función operator+ ha sido modificada de forma que, además de incluir el objeto temporal vr, el argumento ha sido declarado const y pasado por referencia. El resultado es que, además de proporcionar una operatoria correcta, en términos de velocidad de ejecución y memoria requerida, esta solución es mucho más eficaz que la anterior ( 4.2.3).

§2.2  Ejemplo

Finalmente presentaremos una versión análoga al ejemplo aneterior pero utilizando una función-operador (friend) externa a la clase para sobrecargar el operador suma + con miembros de la clase Vector:

#include <iostream>
using namespace std;

class Vector {
  public:
  float x, y;
  friend Vector operator+ (const Vector&, const Vector&);
};

Vector operator+ (const Vector& v1, const Vector& v2) { // función operator+
  Vector vr;     // objeto temporal
  vr.x = v1.x + v2.x;
  vr.y = v1.y + v2.y;
  return vr;
};

int main () {    // ===============
  float x = 5, y = 6;
  cout << "R = " << x + y << endl;
  Vector v1 = {1, 2}, v2 = {3, 4};
  Vector v3 = v1 + v2;
  cout << "Rx = " << v3.x << endl;
  cout << "Ry = " << v3.y << endl;
  cout << "v1.x = " << v1.x << " v1.y = " << v1.y << endl;
}

Por supuesto la salida es exactamente igual que en el caso anterior.

  Inicio.


[1]  En el próximo capítulo veremos que esta circunstancia "indeseada" (en este caso), es precisamente aprovechada cuando se sobrecargan los operadores unarios incremento y decremento ( 4.9.18c).

[2]  Naturalmente estas sentencias tienen una finalidad meramente didácticas y no son sintácticamente correctas.