3

Estoy sobrecargando el operador + externamente. Es obvio que si pongo:

Fraccion operator+ (const Fraccion& r1, const Fraccion& r2)
{
  Fraccion res;

  res.numerador = r1.obtenerNum() + r2.obtenerNum();
  res.denominador = r1.obtenerDen() + r2.obtenerDen();
  return res;
}

res.numerador y res.denominador no son accesibles ya que son miembros privados y sólo se podría llamar a través de una función pública que los devuelva, pero si uso esto:

Fraccion operator+ (const Fraccion& r1, const Fraccion& r2)
{
  Fraccion res;

  res.obtenerNum() = r1.obtenerNum() + r2.obtenerNum();
  res.obtenerDen() = r1.obtenerNum() + r2.obtenerDen();
  return res;
}

Me dice el compilador que no es asignable. Está claro que si esto mismo lo hago sobrecargando como función miembro sí que valdría (la primera función sobrecargada que os pongo con miembros res.numerador) ¿Pero entonces cómo debo poner esas variables para que sí puedan acceder a un valor con esa función sobrecargada externa?

Gracias!

Adjunto la clase Fracción (inventada por mí):

class Fraccion
{
public:
  Fraccion(float n=0, float d=0) : numerador(n), denominador(d) {}
  //Operador = OBLIGATORIA como función interna a la clase
  Fraccion& operator=(const Fraccion& frac);
  Fraccion& operator-(const Fraccion& frac2);
  void mostrarFraccion() const;
  void rellenarFraccion();
  float obtenerNum() const { return numerador; }
  float obtenerDen() const { return denominador; }
private:
  float numerador;
  float denominador;
};
ProgrammerJr
  • 759
  • 5
  • 14
  • En tu función libre ¿El numerador es la suma de ambos numeradores pero el denominador es la suma del numerador y denominador? O__o – PaperBirdMaster Mar 27 '18 at 11:01
  • Razón tienes, lo cambio. Aun así, sé que de por sí, así no se suman fracciones porque en la suma habría que hacer el MCM de los denominadores, pero bueno, tampoco me quiero dar al coco ya que de momento ando con la sobrecarga y bastante tengo jajaj Gracias @Paula_plus_plus – ProgrammerJr Mar 27 '18 at 11:04

2 Answers2

3

Haz que tu función sea amiga de tu clase:

class Fraccion
{
  friend operator+( const Fraccion &, const Fraccion & );

public:
  Fraccion(float n=0, float d=0) : numerador(n), denominador(d) {}
  //Operador = OBLIGATORIA como función interna a la clase
  Fraccion& operator=(const Fraccion& frac);
  Fraccion& operator-(const Fraccion& frac2);
  void mostrarFraccion() const;
  void rellenarFraccion();
  float obtenerNum() const { return numerador; }
  float obtenerDen() const { return denominador; }
private:
  float numerador;
  float denominador;
};

Al declararla friend, permites a las función o clase acceder a los miembros private y public.

Trauma
  • 25,297
  • 4
  • 37
  • 60
  • Buah, es una idea fantástica! Me gustaría saber si esta sería la única solución o habría otra alternativa. Gracias compañero @Trauma – ProgrammerJr Mar 27 '18 at 10:49
3
Fraccion operator+ (const Fraccion& r1, const Fraccion& r2)
{
    Fraccion res;

    res.obtenerNum() = r1.obtenerNum() + r2.obtenerNum();
    res.obtenerDen() = r1.obtenerNum() + r2.obtenerDen();
    return res;
}

Me dice el compilador que no es asignable.

Y es lógico, si miramos la implementación:

float obtenerNum() const { return numerador; }
float obtenerDen() const { return denominador; }

Vemos que tanto obtenerNum como obtenerDen devuelven un VLD (valor del lado derecho RValue en inglés). Los VLD son una categoría de datos que se espera que estén siempre a la derecha de un operador y en consecuencia no pueden usarse a la izquierda de un operador (consulta este hilo para más detalles).

Podrías modificar obtenerNum y obtenerDen para que devolviesen un VLI (valor del lado izquierdo):

const &float &obtenerNum() const { return numerador; }
//    ~ <--- Obtiene una referencia al dato interno
const &float &obtenerDen() const { return denominador; }
//    ~ <--- Obtiene una referencia al dato interno
float &obtenerNum() { return numerador; }
//    ~ <--- Obtiene una referencia al dato interno
float &obtenerDen() { return denominador; }
//    ~ <--- Obtiene una referencia al dato interno

Pero necesitarás las versiones constantes y no-constantes, es una práctica habitual (observa el operador at de std::vector).

De esta manera tu operador libre ya es funcional:

Fraccion operator+ (const Fraccion& r1, const Fraccion& r2)
{
    // res no es constante
    Fraccion res;

    res.obtenerNum() = r1.obtenerNum() + r2.obtenerNum();
//  ~~~ <--- llama a la versión no-constante de Fraccion::obtenerNum
    res.obtenerDen() = r1.obtenerNum() + r2.obtenerDen();
//  ~~~ <--- llama a la versión no-constante de Fraccion::obtenerDen
    return res;
}

Pero este código es más bien confuso, aprovecha que tienes un constructor que recibe numerador y denominador para construir tu objeto Fraccion en el momento de devolverlo y aprovechar la optimización de valor de retorno:

Fraccion operator+ (const Fraccion& r1, const Fraccion& r2)
{
    return { r1.obtenerNum() + r2.obtenerNum(), r1.obtenerNum() + r2.obtenerDen() };
}

El código es mucho más sencillo así. Claro que también puedes usar una función friend como sugiere Trauma.

PaperBirdMaster
  • 44,474
  • 6
  • 44
  • 82
  • jeje ... aún faltaría alguna mas ... ¿ Dejamos el `PassKey` para @eferion ? Ah no, que no se puede usar con operadores :-( – Trauma Mar 27 '18 at 11:03