Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: marlboreano en 6 Marzo 2015, 15:38 pm



Título: [?][C++] Vectores, strings y archivos con clase - C++
Publicado por: marlboreano en 6 Marzo 2015, 15:38 pm
Hola a todos; me encuentro con un grave problema (que raramente no vi mientras hacía mi proyecto). En fin, tengo dos clases (en el proyecto, tengo unas cuaaantas más, pero a modo explicativo y para resumir, solo 2 por acá): "Madre" e "Hija", que se relacionan por composición (Una madre tiene un vector (stl) de hijas). El tema es que también en su composición tienen strings (para almacenar los nombres en este caso) y tengo un vector que uso desde el main que es de tipo Madre.
La idea es poder guardar esos datos en un archivo para poder consultarlo luego más tarde (en el vector voy guardando Madres [con sus respectivas hijas]), pero me encuentro con que si quiero guardar un vector en un fichero, solo guardo 'basura' o 'posibles direcciones', nada de lo que me interese.
Les dejo el código de ejemplo que hice, para ver si me pueden dar una mano y/o una guía para revisar, ya que, si bien en este caso las clases son bastante simples (y hasta podrían heredarse), en mi proyecto original son bastante más complicadas y no deseo re-diseñar todas las mismas, ya que me llevaría mucho más tiempo que encontrarle una solución a esto.

Madre.h
Código
  1. #ifndef MADRE_H
  2. #define MADRE_H
  3. #include "Hija.h"
  4. #include <vector>
  5. #include <string>
  6. using namespace std;
  7.  
  8. class Madre {
  9. private:
  10. string nombre;
  11. int edad;
  12. vector<Hija> susHijas;
  13. protected:
  14. public:
  15. //Constructor
  16. Madre();
  17. //Consultores
  18. int MostrarEdad(void);
  19. string MostrarNombre(void);
  20. Hija MostrarHija(const int&);
  21. int CantidadHijas(void);
  22. //Cargadores
  23. bool CargarEdad(const int&_edad);
  24. bool CargarNombre(const string&_nombre);
  25. bool CargarHija(const Hija &_hija);
  26. bool EliminarHija(const int &_indice);
  27. bool CargarDatos(const string &_nombre, const int &_edad);
  28. //Destructor
  29. ~Madre();
  30. };
  31.  
  32. #endif

Madre.cpp
Código
  1. #include "Madre.h"
  2. #include <sstream>
  3. using namespace std;
  4.  
  5. Madre::Madre(void) : nombre("N/N"), edad(-1) {
  6. susHijas.clear();
  7. }
  8.  
  9. Madre::~Madre(void) {
  10.  
  11. }
  12.  
  13. int Madre::MostrarEdad(void) {
  14. return edad;
  15. }
  16.  
  17. string Madre::MostrarNombre(void) {
  18. return nombre;
  19. }
  20.  
  21. bool Madre::CargarEdad(const int & _edad) {
  22. edad = _edad;
  23. return (edad==_edad)?true:false;
  24. }
  25.  
  26. bool Madre::CargarNombre(const string & _nombre) {
  27. nombre = _nombre;
  28. return (nombre==_nombre)?true:false;
  29. }
  30.  
  31. Hija  Madre::MostrarHija(const int &_indice) {
  32. if (unsigned(_indice)>susHijas.size() || susHijas.empty()) {
  33. Hija aux;
  34. return aux;
  35. }
  36. return susHijas[_indice];
  37. }
  38.  
  39. bool Madre::CargarHija(const Hija &_hija) {
  40. susHijas.push_back(_hija);
  41. return true;
  42. }
  43.  
  44. bool Madre::EliminarHija(const int &_indice) {
  45. if (unsigned(_indice)>susHijas.size()) {
  46. return false;
  47. }
  48. susHijas.erase(susHijas.begin()+_indice);
  49. return true;
  50. }
  51.  
  52. int Madre::CantidadHijas(void) {
  53. return int(susHijas.size());
  54. }
  55.  
  56. bool Madre::CargarDatos(const string &_nombre, const int &_edad) {
  57. nombre = _nombre;
  58. edad = _edad;
  59. return true;
  60. }

Hija.h
Código
  1. #ifndef HIJA_H
  2. #define HIJA_H
  3. #include <string>
  4. using namespace std;
  5.  
  6. class Hija {
  7. private:
  8. string nombre;
  9. int edad;
  10. protected:
  11. public:
  12. //Constructor
  13. Hija();
  14. //Consultores
  15. int MostrarEdad(void);
  16. string MostrarNombre(void);
  17. //Cargadores
  18. bool CargarEdad(const int&_edad);
  19. bool CargarNombre(const string&_nombre);
  20. bool CargarDatos(const string &_nombre, const int &_edad);
  21. //Destructor
  22. ~Hija();
  23. };
  24.  
  25. #endif

Hija.cpp
Código
  1. #include "Hija.h"
  2.  
  3. Hija::Hija() : nombre("N/N"), edad(-1) {
  4.  
  5. }
  6.  
  7. Hija::~Hija() {
  8.  
  9. }
  10.  
  11.  
  12. int Hija::MostrarEdad(void) {
  13. return edad;
  14. }
  15.  
  16. string Hija::MostrarNombre(void) {
  17. return nombre;
  18. }
  19.  
  20. bool Hija::CargarEdad(const int & _edad) {
  21. edad = _edad;
  22. return (edad==_edad)?true:false;
  23. }
  24.  
  25. bool Hija::CargarNombre(const string & _nombre) {
  26. nombre = _nombre;
  27. return (nombre==_nombre)?true:false;
  28. }
  29.  
  30. bool Hija::CargarDatos(const string &_nombre, const int &_edad) {
  31. nombre = _nombre;
  32. edad = _edad;
  33. return true;
  34. }

Main.cpp
Código
  1. #include<iostream>
  2. #include <string>
  3. #include <fstream>
  4. #include "Madre.h"
  5. #include <sstream>
  6. #include "Hija.h"
  7. using namespace std;
  8.  
  9. ostream& operator<<(ostream &os, Madre &_madre) {
  10. stringstream aux;
  11. if (!_madre.CantidadHijas()==0) {
  12. aux<<" y tiene "<<_madre.CantidadHijas()<<" hijas";
  13. } else {
  14. aux<<" y no tiene hijas";
  15. }
  16. return os<<_madre.MostrarNombre()<<" - "<<_madre.MostrarEdad()<<aux.str();
  17. }
  18.  
  19. ostream& operator<<(ostream &os, Hija &_hija) {
  20. return os<<_hija.MostrarNombre()<<" - "<<_hija.MostrarEdad();
  21. }
  22.  
  23. int main (int argc, char *argv[]) {
  24. //Creo y cargo los datos de una madre
  25. Madre m1;
  26. m1.CargarDatos("Laura",40);
  27. //Creo y cargo los datos de una hija
  28. Hija h1;
  29. h1.CargarDatos("Litoral",15);
  30. //Inserto la hija en el vector de la Madre
  31. m1.CargarHija(h1);
  32. //Muestro los datos de la madre
  33. cout<<m1<<endl;
  34. //Muestro los datos de la Hija
  35. cout<<h1<<endl;
  36.  
  37. //Creo un vector de madres para trabajar con ellas
  38. vector<Madre> vMadres;
  39. //Cargo una madre al mismo
  40. vMadres.push_back(m1);
  41. //¿Guardar?
  42. return 0;
  43. }

Desde ya, muchas gracias por su comprensión, enseñanza, queja o cualquier aporte.

Nota: El último comentario y su raro "&#191;" es porque soy hispanohablante orgulloso, y utilizo mis signos "¿" cuando y cómo yo quiero, si ASCII no lo toma, problema suyo.
Nota 2: Si, se que sería raro ponerle de nombre "Litoral" a una hija, pero es así cuando uno tiene la cabeza nublada y no se le ocurre nada  :xD.


Título: Re: Vectores, strings y archivos con clase - C++
Publicado por: ivancea96 en 6 Marzo 2015, 16:12 pm
Para guardar un vector en un archivo, serializalo. Conviertelo a una cadena de caracteres, con el formato que prefieras.
Luego, claro está, tendrás que hacer un método para deserializarlo, transformando la cadena, en un vector.

EDITO:
Si solo quieres guardar los datos en el archivo sin más, haz un ciclo por cada dato del vector, y lo pasas al archivo.


Título: Re: Vectores, strings y archivos con clase - C++
Publicado por: marlboreano en 6 Marzo 2015, 16:36 pm
Gracias por tu tan pronta respuesta, ivancea96, el tema es que según veo en la documentación sería "ilegal" intentar guardarme los string de cada objeto.

Mejor un ejemplo porque explicando soy un queso:
Código
  1. ofstream salida("datos.dat",ios::binary|ios::trunc);
  2. if (!salida.is_open()) {
  3. cerr << "No se pudo guardar" <<endl;
  4. }
  5. int ind = 0;
  6. while(salida.write(reinterpret_cast<char*>(&vMadres[ind]),sizeof(Madre))) {
  7. ind++;
  8. }
  9. salida.close();

Esto, en teoría, no me guardaría los nombres de cada clase (Madres e Hijas). Ese es mi principal problema.
Además, tampoco me podría guardar el vector entero, que sería de lo más sencillo :/.

Desde ya, muchas gracias.


Título: Re: Vectores, strings y archivos con clase - C++
Publicado por: ivancea96 en 6 Marzo 2015, 16:56 pm
Con serializarlo, me refiero a serializarlo tú:

Código
  1. ofstream out = /*...*/;
  2. vector<string> v = /*...*/;
  3. size_t tam = v.size();
  4. out.write((char*)&tam, sizeof(tam));
  5. for(string& s:v){
  6.    tam = s.size();
  7.    out.write((char*)&tam, sizeof(tam));
  8.    out.write(s.c_str(), tam);
  9. }

Vector:
Código:
<NUM_STRINGS><string1><string2><...>

Y luego cada string:
Código:
<NUM_CHARS><chars_array>


Título: Re: Vectores, strings y archivos con clase - C++
Publicado por: marlboreano en 7 Marzo 2015, 14:10 pm
Disculpame, ivancea96, pero no logro entender la sintaxis (estoy aprendiendo según el standard C++99 aún). Se que las dos primeras líneas son a modo explicativo no más, ya que no es legal ese tipo de declaración, como también que el casteo es ambiguo (me recomendaron en la facultad siempre utlizar el "reinterpretcast<char*>" y no el casteo que utilizaba (char*), pero me pierdo en las líneas 4, 7 y 8.

Línea 4:
Código
  1. out.write((char*)&tam, sizeof(tam));

Esto le pasa al flujo un size_t interpretado como char* con su tamaño respectivo en bytes.
¿Para qué?
Otra: Si un size_t es un entero sin signo que interpreta bytes, ¿para qué hacerle un sizeof? ¿O lo estarás utilizando como un unsigned int? (lo digo desde mi más completa ignorancia, nada es para ofender sino para aprender)

Línea 7:
Código
  1. out.write((char*)&tam, sizeof(tam));

Lo mismo que antes (aunque a esta altura del código, tam ahora equivale al tamaño del string 's' y no del vector 'v').

Línea 8:
Código
  1. out.write(s.c_str(), tam);

Acá si entiendo "algo". Le pasas al flujo el string 's' convertido a char, con su tamaño.

Por el tipo de bucle for, me di cuenta que tu código ejemplo se rige bajo el standard C++11, que tiene varias ventajas, pero como los compiladores viejos interpretan según el C++99, todavía sigo con este pateando.

Otra cosa, si me podrías facilitar algún enlace, título de material o demás sobre serialización (Español o Inglés) de C++, te estaría agradecido eternamente, ya que lo que encuentro es para Java o Python (creo) y, si bien la sintaxis no es muy difícil de comprender, a veces uno se termina perdiendo.

Desde ya, te pido disculpas. Cuando estaba leyendo tu respuesta se cayó mi servicio de internet, tuve un par de problemitas y ayer a la noche pude volver a conectarme, pero no tuve tiempo de responder. Muchas gracias por tus aclaraciones :).
Saludos.


Título: Re: Vectores, strings y archivos con clase - C++
Publicado por: ivancea96 en 7 Marzo 2015, 14:23 pm
Código
  1. out.write((char*)&tam, sizeof(tam));

Veamos. size_t es un unsigned int, 4 bytes. Al hacer &tam, saco la dirección (size_t*), y con (char*), lo paso a un arreglo de char, eso está claro.
Si hago eso, es porque quiero escribir en 'out', los datos binarios del size_t.

Suponiendo que tam=5. en hexadecimal sería: 0x00000005. Si hago "out << tam", va a escribir un '5' en el archivo. Pero eso no me interesa al serializar, ya que en decimal, el número ocupa más memoria. Así que haciendo el casting a char*, convierto el número 0x00000005 a un array de char {0x00, 0x00, 0x00, 0x05}. Así es como lo quiero poner en el archivo: 4 caracteres.

El sizeof(tam), equivalente a sizeof(size_t), es para saber el tamaño en bytes, ya que se lo tengo que pasar a la función write. En este caso, el tamaño es 4.


Si en la facultad te dicen de usar reinterpret_cast, puedes usarlo. Funcionará igual.


Acerca de lo de serializar en Java, cuidado, porque Java suele tener métodos propios para serializar clases estandar (pese a que puedas hacerlos también por ti mismo.

No se de tutoriales para serializar, lo único importante es el concepto: convertir una clase a un arreglo de bytes. (Y poder revertirlo luego)


Título: Re: Vectores, strings y archivos con clase - C++
Publicado por: marlboreano en 7 Marzo 2015, 14:37 pm
Muchas gracias ivancea96 por la explicación y la predisposición. Voy a seguir buscando en Google todo lo que me sirva para poder serializar mis clases (estoy en medio de un proyecto y lo único que me falta es guardar [y cargar, obviamente] los datos que manejo desde un programa cliente).

Saludos :).