¡Nuevo!  por fin disponible la versión 5 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.