[C con Clase] Sobrecarga del operador >> y herencia

Steven Davidson srd4121 en njit.edu
Vie Feb 19 22:16:54 CET 2010


Hola Armando,

abvera en arnet.com.ar wrote:
> class A{
>         ...
>         // Sobrecarga del operador >>
>         friend std::istream& operator>>(std::istream& ain, A& a);
> }
> 
> class B{
>         ...
>         // Sobrecarga del operador >>
>         friend std::istream& operator>>(std::istream& bin, B& b);
> }
> 
> 
> class X : public A, public B {
>     ...
> 
> }
> 
> Las clases A y B compilan separadamente. No logro compilar la clase
> X. Cada uno en su propio archivo de definición .cpp
> 

Tienes que definir las otras dos clases, 'A' y 'B'. Por ejemplo,

// claseA.h

class A { ... };


// claseB.h

class B { ... };


// claseX.h

#include "claseA.h"
#include "claseB.h"

class X : public A, public B { ... };


> la pregunta es ¿Hay alguna forma de lograr el siguiente
> comportamiento:?
> 
> X >> miX;
> y automaticamente se llame a
> 
>         A >> miX;
>         B >> miX;
> o viceversa.
> 

Ten presente que la amistad NO se hereda. Lo que sí podemos hacer es 
convertir temporalmente el tipo del objeto 'X' a uno de los tipos en la 
jerarquía. Por ejemplo,

A objA = static_cast< A >( miX );
is >> objA;

Posiblemente, perdamos información al tratar el objeto 'miX' de la clase 
'X' como un objeto de la clase 'A'.

> Al intentar compilar X, el error que me tira es sobrecarga ambigua. Y
> de hecho el compilador no sabe cual de las dos definiciones de
> sobrecarga tiene prioridad.
> 

Puedes solucionar el problema de la ambigüedad con un cásting explícito 
para determinar el "camino" a seguir.

> Una solución sería sobrecargar X y no hacer en A y B, pero es más 
> laborioso. Se me ocurre que podría haber alguna tenica que me permita
> aprovechar la sobrecarga de las clases bases.
> 

Ten en cuenta que la sobrecarga de la clase base posiblemente no te 
sirva para cualesquier objetos de la clase derivada. Recuerda que los 
objetos de la clase derivada suelen contener más información que los de 
la clase base. Por lo tanto, la sobrecarga que trata la clase base no 
manipulará la información añadida del objeto de la clase derivada. Por 
ejemplo,

class Persona
{
private:
   string sNombre;
   string sApellido1;
   string sApellido2;
...
};

class Empleado : public Persona
{
private:
   float fSueldo;
...
};

ostream &operator<<( ostream &os, const Persona &ref )
{
   return os << ref.nombre() << ' '
             << ref.apellido1() << ' '
             << ref.apellido2();
}
...
Empleado obj( "Pepe", "Alvarez", "Dominguez", 12345 );

cout << static_cast< Persona >( obj ) << endl;


El problema es que mostrará la información de 'obj' como un objeto de la 
clase 'Persona'. Aparecerá esto en la pantalla,

Pepe Alvarez Dominguez

Como ves, no aparece la otra información de un 'Empleado'. Por ello, 
solemos crear otra sobrecarga para esta otra clase. Por ejemplo,

ostream &operator<<( ostream &os, const Empleado &ref )
{
   return os << ref.nombre() << ' '
             << ref.apellido1() << ' '
             << ref.apellido2() << ';'
             << ref.sueldo();
}

Ahora podemos usar la versión correcta para 'Empleado'. Esto es,

cout << obj << endl;

Aparecerá lo siguiente en pantalla:

Pepe Alvarez Dominguez;12345

Ahora bien, desde un punto de vista de diseño, estamos reescribiendo el 
mismo comportamiento de 'operator<<(ostream &, const Persona &)' para 
'operator<<(ostream &, const Empleado &)'. Por lo tanto, podríamos hacer 
lo siguiente:

ostream &operator<<( ostream &os, const Empleado &ref )
{
   return os << static_cast< const Persona & >( obj ) << ';'
             << ref.sueldo();
}

De esta manera, usamos el comportamiento ya implementado para 'Persona' 
y simplemente agregamos el otro comportamiento a implementar para 
'Empleado'. Así no tenemos que reescribir el mismo comportamiento.

Obviamente, esto no siempre funciona, porque el comportamiento para una 
clase derivada no tiene por qué tener relación alguna con el de la clase 
base.

> ----------
> El error que me tira el compilador es el siguiente, pero las clases
> no son las misma de ejemplo.
> 
> pprof.cpp: In function ‘int main()’:
> pprof.cpp:38: error: ambiguous overload para ‘operator>>’ en ‘std::cin 
>  >> yo’
> CTitulo.h:48: nota: los candidatos son: std::istream&
> operator>>(std::istream&, CTitulo&)
> CPersona.h:53: nota:                     std::istream&
> operator>>(std::istream&, CPersona&)
> pprof.cpp:39: error: ambiguous overload para ‘operator<<’ en ‘std::cout 
> << yo’
> CTitulo.h:47: nota: los candidatos son: std::ostream&
> operator<<(std::ostream&, CTitulo&)
> CPersona.h:52: nota:                     std::ostream&
> operator<<(std::ostream&, CPersona&)
> -------------------
> 

Estos errores tienen que ver con el hecho de que el compilador no puede 
decidir por cuál "camino" escoger. En este caso, el compilador puede 
elegir manipular el objeto 'yo' como una referencia a la clase 'CTitulo' 
y así invocar 'operator>>(std::istream &, CTitulo &)'. Por otro lado, se 
puede manipular el objeto 'yo' como una referencia a la clase 'CPersona' 
y así invocar 'operator>>(std::istream &, CPersona &)'.

La solución es elegir nosotros mismos el tratamiento del objeto. Esto 
significa que realizaremos un cásting explícito a una clase base u otra. 
Claro que la otra solución es implementar otra sobrecarga para aceptar 
objetos de la clase derivada.


Espero haber aclarado la duda.

Steven





Más información sobre la lista de distribución Cconclase