2010-11-30 23 views
5

Sé que puedo utilizar:C++ guardar y cargar el juego con los punteros en estructuras

MyGame game; // the game object 
// 

ofstream out("mygame.bin", ios::binary); 
out.write((char *)&game, sizeof(MyGame)); 

de guardar y cargar el juego, pero lo que si tengo punteros dentro de la estructura MyGame? ¿se guardarán los indicadores pero no los datos a los que apunta?

y: cómo solucionarlo?

Respuesta

5

No se puede escribir punteros a una corriente y esperar que se hará mágicamente. Necesita implementar métodos de guardar/cargar en sus objetos. Por ejemplo:

class Serializable 
{ 
    virtual void save(std::ofstream& _out) const = 0; 
    virtual void load(std::ifstream& _in) = 0; 
}; // eo class Serializable 


// some game object 
class MyObject : public Serializable 
{ 
    int myInt; 
    std::string myString; 

    virtual void save(std::ofstream& _out) const 
    { 
     _out << myInt << myString; 
    }; // eo save 

    virtual void load(std::ifstream& _in) 
    { 
     _in >> myInt >> myString; 
    }; // eo load 
}; // eo class SomeObject 

class MyGame : public Serializable 
{ 
    MyObject a; 
    MyObject b; 

    virtual void save(std::ofstream& _out) const 
    { 
     a.save(_out); 
     b.save(_out); 
    }; // eo save 

    virtual void load(std::ifstream& _in) 
    { 
     a.load(_in); 
     b.load(_in); 
    }; // eo load 
}; // eo class MyGame 
+0

esto no es realmente binario ¿verdad?;) – Nim

+0

también, una secuencia requiere un separador para separar los campos (generalmente un espacio), en este ejemplo falta en la operación de salida de flujo, por lo tanto, la secuencia en funcionamiento simplemente lanzará una excepción cuando intente leer el entero (I pensar, no lo he probado, así que podría estar equivocado) – Nim

+0

Esto no lo va a cortar. En primer lugar, usted está "haciendo trampa" aquí porque no está serializando ningún puntero como lo requiere el OP. Además, está asumiendo que los operadores << and >> harán el truco, cuando es mucho más robusto controlar la serialización de primitivos usted mismo (por ejemplo, qué se hace con htonl() y ntohl() con respecto a la transmisión de datos a través de una red) . – Jon

1

Se podría sobrecargar el flujo de salida del operador (<<) y corriente a cabo cada campo individual (y viceversa)

EDIT: aquí es un ejemplo completo ...

#include <iostream> 
#include <fstream> 
#include <map> 

using namespace std; 

template <typename T> 
void serialize(ostream& str, const T& field) 
{ 
    str.rdbuf()->sputn(reinterpret_cast<const char*>(&field), sizeof(T)); 
} 

template <typename T> 
void deserialize(istream& str, T& field) 
{ 
    str.rdbuf()->sgetn(reinterpret_cast<char*>(&field), sizeof(T)); 
} 

class MyGame 
{ 
public: 
MyGame() : a(), b() {} 
MyGame(int av, int bv) : a(av), b(bv) {} 

friend ostream& operator<<(ostream& str, MyGame const& game); 
friend istream& operator>>(istream& str, MyGame& game); 

    int getA() const { return a; } 
    int getB() const { return b; } 

private: 
int a; 
int b; 
}; 

ostream& operator<<(ostream& str, MyGame const& game) 
{ 
    serialize(str, game.a); 
    serialize(str, game.b); 
    return str; 
} 

istream& operator>>(istream& str, MyGame& game) 
{ 
    deserialize(str, game.a); 
    deserialize(str, game.b); 
    return str; 
} 

int main(void) 
{ 
    { 
    ofstream fout("test.bin", ios::binary); 
    MyGame game(10, 11); 
    fout << game; 
    } 

    { 
    ifstream fin("test.bin", ios::binary); 
    MyGame game; 
    fin >> game; 
    cout << "game.a: " << game.getA() << ", game.b: " << game.getB() << endl; 
    } 

    return 0; 
} 

Debe comprender el problemas con este enfoque, como el archivo resultante será específico de la plataforma (es decir, no portátil) etc.

0

Pruebe game.serialize(out);. En su función de miembro de serialización, llame en serie a los miembros de su puntero.

0

Realice una función de serialización por tipo que debe ser persistente.
Llamar a este para cada miembro.

En realidad es similar a la serialización en red o la visualización con fines de depuración.

boost.serialize puede ayudarlo.

2

Suponiendo que no ha reemplazado a char * cast, sí, es muy probable que esto solo guarde el puntero y no los datos.

Lo que necesita es la serialización de su objeto. Puede proporcionar un método para calcular el estado del objeto en un flujo de bits y escribirlo. Y también necesita tener un método para restablecer el estado.

Usted puede leer más acerca de la serialización en wikipedia

+0

Como se menciona a continuación, sus métodos de serialización y restauración pueden anularse << and >> operadores! –

1

Boost tiene un serialization library, con soporte incorporado para puntero profunda guardar y restaurar, y la serialización adecuado de punteros a los datos compartidos.

Es una biblioteca bastante extensa, pero no necesita escribir tanto código para comenzar a usarla en sus propios proyectos. Bien vale la pena el esfuerzo de aprendizaje para cualquier cosa que no sean los requisitos de serialización más simples en mi opinión.

0

La serialización "ingenua" que acaba de volcar el valor de los punteros nunca va a funcionar porque, al deserializar, esos punteros no serán válidos.

El enfoque general de este tipo de problema sería algo así:

  1. que cada objeto que puede ser serializado en su juego de implementar una función virtual serialize() (estoy asumiendo que todos estos objetos en última instancia, se derivan de la misma clase base).
  2. Haga que la clase base implemente una función pública get_serialized_id().Esta función utilizará una variable estática autoincrementada para generar un id. Único para cada instancia de objeto, pero solo la primera vez que se invoque (las llamadas subsiguientes devolverán el valor existente).

Ahora, cuando la serialización:

  1. de inicio con un std::map<int, YourBaseClass*>. Agregue su objeto game a este mapa, utilizando el valor devuelto por get_serialized_id() para la clave.
  2. Mientras que el mapa contiene objetos que aún no se han serializado:
    • Tome el primer objeto.
    • Serializar su get_serialized_id().
    • Serialícelo llamando a su implementación para serialize(). Que persista sus datos primitivos como de costumbre. Para los datos disponibles a través de punteros, llame al get_serialized_id() en cada objeto señalado y simplemente serialice el número devuelto desde él. Además, agrega ese objeto al mapa.

Esto resultará en un montón de objetos que están siendo serializado (en un orden "aleatorio") junto con de cada uno id "aleatorio".

Cuando deserializar:

  1. de inicio con un std::map<int, YourBaseClass*>. Lea el primer elemento en su archivo guardado.
  2. Para cada objeto al que apunta este primer objeto, conoce una identificación única (esto es lo que serializó en lugar de un puntero). Recupera el elemento con esta identificación del archivo guardado y deserialízalo.
  3. Realice esto recursivamente hasta que todos los elementos se hayan recuperado y deserializado del archivo guardado.
  4. Como cada elemento tiene todas sus dependencias deserializadas en el paso 3 anterior, ejemplifíquelo como un objeto y agréguelo al mapa.
  5. Esto le permitirá tomar un puntero del mapa dado el id de un elemento, que ahora puede usar para establecer los punteros de los objetos que dependen de este elemento.
  6. Cuando finaliza la recursividad, el último objeto en el mapa será su objeto principal de "juego" con todos sus indicadores listos para funcionar.
0

Lo que hizo es una copia superficial, si tiene punteros en su clase MyGame, entonces una copia profunda es IMPRESCINDIBLE. Sugiero implementar una función o un conjunto de funciones dentro de MyGame que se encargará de guardar sus propios datos en un archivo, y solo tendrá que llamarlo.

0

Gracias a todos por las rápidas y buenas respuestas, pero un amigo mío (que me está ayudando en esto) me dijo que deberíamos hacerlo de otra manera.

Guardo los conceptos básicos del objeto y recitamos el resto en una función.

Es un juego de cartas y para guardar la pila de cartas guardaremos el ID de la carta solamente (no los objetos) y solo reiniciamos cada carta cuando leemos la ID del archivo.

Cuestiones relacionadas