[C con Clase] FICHERO DE OBJETOS

Steven R. Davidson vze266ft en verizon.net
Mar Nov 21 21:13:07 CET 2006


Hola Juan,

Juan wrote:
> 
> Hola a tod en s <mailto:tod en s>!!
>  
> No encuentro información al respecto (ficheros de objetos)...
>  
> Mi programa: una guia de restaurantes ( Restaurante **R (hay hefrencia y 
> polimorfismo, bares de tapas, con espectáculo y de comida rápida... ) ...
>  
> Si, es una práctica de clase. Si, me compila y funciona a la 
> perfección... Ahora quisiera ampliar mis conocimientos e intentar salvar 
> los datos de cada sesión del programa en un fichero, sobre todo porque 
> cada vez que quiero demostrar que funciona tengo que volver a meter 
> todos los datos de cada restaurante...Es un rollo. Quizá así consiga 
> implementar una guia mejor y poder buscar restaurantes... ampliar... 
> Bueno, he modificado el código que había aqui:
>  
> http://www.conclase.net/c/curso/index.php?cap=039
>  
> de esta manera....
>  

Veamos el código fuente.

> 
> void Guia::GuardarEnFichero()
> {   
>       ofstream fsalida("Guia.dat", ios::out | ios::binary);
>       for (int i=0; i<nr; i++)
>       {
>           fsalida.write
>           (reinterpret_cast<char *>(&R[i]),
>            
> sizeof(ConEspectaculo)||sizeof(ComidaRapida)||sizeof(BaresTapas));
>       }

Esto no es correcto. En primer lugar, debes pasar la cantidad total de 
'char' o sea bytes de la información que pasas como el primer parámetro. 
Usas el operador lógico/booleano OR en lugar de la suma. Esto es un 
error. En principio, esto debería ser:

sizeof(ConEspectaculo) + sizeof(ComidaRapida) + sizeof(BaresTapas)

En segundo lugar, la suma de los tamaños de los miembros de una 
estructura NO es igual al tamaño de la estructura. O sea,

sizeof R[i] != sizeof(ConEspectaculo) + sizeof(ComidaRapida) + 
sizeof(BaresTapas)

Es posible que lo sea, pero en general, no es así.

En tercer lugar, has mencionado que 'R' es un objeto polimórfico. Esto 
implica que hay más información que la que tú definiste. En general, no 
es aconsejable enviar toda la información de un objeto, especialmente 
polimórfico. Tienes dos opciones:

1. Acceder a cada dato miembro del objeto para escribirlo en el fichero. 
Por ejemplo,

fsalida.write( reinterpret_cast< char * >( &R[i].espectaculo ),
                sizeof(ConEspectaculo) );
fsalida.write( reinterpret_cast< char * >( &R[i].comida_rapida ),
                sizeof(ComidaRapida) );
fsalida.write( reinterpret_cast< char * >( &R[i].bares_tapas ),
                sizeof(BaresTapas) );

2. Crear una estructura interna a la clase para agrupar todos los datos 
miembros. Por ejemplo,

class Restaurante
{
public:
   struct datos
   {
     ConEspectaculo espectaculo;
     ComidaRapida comida_rapida;
     BaresTapas bares_tapas;
   };

private:
   datos info;
   ...
};

De esta manera, tienes toda la información en una estructura sin tener 
otros miembros "agregados". Entonces puedes hacer una sola escritura; 
esto es,

fsalida.write( reinterpret_cast< char * >( &R[i].info ),
                sizeof(Restaurante::datos) );

Por último, recomiendo que cada objeto polimórfico defina la forma de 
"comunicarse" con los canales o flujos de entrada y salida. Por ejemplo,

// Clase polimórfica
class Restaurante
{
   ...
   virtual ostream & escribir( ostream &is );
   virtual istream & leer( istream &is );
};

De esta manera, sólo tienes que invocar las funciones miembros virtuales 
que te interesan dejando la implementación bajo la responsabilidad de 
cada clase en la jerarquía. Esto sería,

for( int i=0; i<nr; i++ )
   R[i].escribir( fsalida );
...
for( int i=0; i<nr; i++ )
   R[i].leer( fentrada );

Como desconozco la definición de 'Restaurante', no puedo comentar mucho 
más. Sin embargo, quiero dejar claro que si tienes punteros en tal 
clase/estructura, deberás acceder a la información apuntada para poder 
manipularla con los ficheros, ya sea leyendo o escribiendo.

>       fsalida.close();   
> }
> 
> void Guia::LeerFichero()
> {   
>       ifstream fentrada("Guia.dat", ios::in | ios::binary);
>       for (int i=0; i<nr; i++)
>       {
>           fentrada.read
>           (reinterpret_cast<char *>(&R[i]),
>            
> sizeof(ConEspectaculo)||sizeof(ComidaRapida)||sizeof(BaresTapas));
>       }
>       fentrada.close();  
> }
> 
> El programa completo compila (incluido este código que esta dentro de 
> guia.cpp)
>  
> El programa ppal empieza  asi:
> int main()
> {
>     Guia G;
>     G.LeerFichero();
> ...
>  
> //y acaba así:
>  
>     G.GuardarEnFichero(); 
>     delete &G;

Esto no tiene sentido, ya que 'G' es un objeto que fue instanciado 
automáticamente y no a través del operador 'new'. Recuerda que al 
terminar el ámbito de un objeto, éste es automáticamente destruido 
invocando su destructor. Por lo tanto, no es necesario destruir el 
objeto explícitamente.


Espero que todo esto te ayude.

Steven







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