Objekte auf dem Heap sichern



  • Hey ,

    ich habe eine kleine Frage also :

    void funktion::status_save(klasse *object)

    ich habe einer funktion von einer klasse ein object das auf dem heap liegt übergeben . Jetzt möchte ich dieses Objekt in eine FIle sichern :

    geh ich richtig in der Annahme das ich das so machen muss ?

    nfout.write((char*)(&wPlayer),sizeof(wPlayer));

    und zum einlesen einfach in einer anderen Funktion ein object der klasse "klasse" erzuegen zb :

    Klasse Objekt;

    nfin.read((char*)(&Objekt), sizeof(Objekt));

    weil wenn ich das so sichere und dann wieder einlese und ausgebe gibt er irgendwelche Zahlen aus die gar nicht in der Klasse gesichert sind
    Muss ich vielleicht beim Sichern oben etwas anderes schreiben also das Objekt anders vom Heap ziehen ?
    Ihr könnt es ja selbst ausprobieren macht euch eine klasse weißt dieser Daten zu und benutzt die oben stehenden zeilen , also bei mir gibt er beim auslesen nicht das richtige aus .. eventuell liest er schon das falsche ein . Wie kann ich denn eine Variable die in einem Object ist das im heap steht ausgeben mit objekt -> funktion() kann man ja nur auf funktionen zugreifen ... 😕

    Hoffe ihr könnt mir helfen...



  • & entfernen 🙄



  • und sizeof(klasse) benutzen



  • hola

    so kannst du objekte maximal dann schreiben bzw. lesen wenn deine klasse nur aus einfachen datentypen (pods) wie int, float, char, bool ... bestehen.

    class klasse
    {
       public:
          int integer;
          float real; 
    };
    

    mit so ner klasse wuerde es gehen.

    sobald du jedoch pointer, std::strings oder aehnliches in der klasse verwendest klappt das schon nicht mehr, weil die daten nicht hintereinander im speicher liegen. dann bekommst du nur muell. in dem fall musst du die daten serialisieren und dann in eine datei schreiben.

    class klasse
    {
       public:
          int integer;
          float real; 
          std::string str;
    };
    
    std::ostream& operator<<(std::ostream &out, const klasse &kl)
    {
       out.write(reinterpret_cast<const char*>(kl.integer), sizeof(kl.integer));
       out.write(reinterpret_cast<const char*>(kl.real), sizeof(real));
       std::size_t len = kl.str.size(); 
       out.write(reinterpret_cast<const char*>(len), sizeof(len));
       out.write(kl.str.c_str(), len);
       return out.
    }
    
    // verwendung:
    klasse kl;
    nfout << kl;
    

    binaerdaten zu speichern kann natuerlich auch probleme machen, falls die datei auf einem anderen system gelesen werden soll oder du selbst z.b. von einem Win32 auf ein Win64 system umsteigen solltest, da sich die groessen der datentypen aendern kann.

    Meep Meep



  • Klappt irgendwie immer noch nicht , ich teste beim laden ja immer und lass mir einzelne daten der klasse ausgeben und da bekomm ich nur chineesische zeichen xD

    also ich versuch ma ganz genau zu zeigen was ich habe

    ich habe eine kLasse :

    class sPlayer
    {
          public:
           std::string name;
           std::string Typ;
           int irgendwas;
           float money;
    };
    

    dann habe ich eine FUnktion in der die Datenzugewiesen werden :

    void cWorld::enterworld(int _irgendwas , string _name , string _Typ)
    {
        sPlayer wPlayer;
    
        wPlayer.name= _name;
        wPlayer.irgendwas = _irgendwas;
        wPlayer.Typ = _Typ;
        wPlayer.money = 300.00;
    
    }
    

    jetzt möchte ich die Klasse sPlayer in der die Daten alle stehen sichern :

    void cWorld::status_save(sPlayer *wPlayer)
    {
    ofstream nfout(file_name.c_str(), ios::binary );
    nfout.write((char*)(wPlayer),sizeof(sPlayer));
    nfout.close();
    }
    

    und später wenn man das Programm zum Beispiel wieder startet laden :

    void cWorld::LoadPlayer()
    {
    sPlayer  wPlayer;
    ifstream nfin (dateiname.c_str(), ios::binary);   //dateiname ist definiert
    nfin.read((char*)(&wPlayer), sizeof(sPlayer));
    nfin.close();
    

    so wenn ich es jetzt ausgeben will kommen total komische Zeichen :
    ausgeben tu ich es so :

    cout << wPlayer.Typ << endl;  // Zum beipspiel
    

    Ich hoffe ihr versteht jetzt wie ich es machen will :

    Ich vermute die fehler liegen beim speichern , laden , oder bei der übergabe des Pointer objekts an die funktion status_save .....



  • du hast mein posting schon gelesen oder ?

    class sPlayer
    {
          public:
           std::string name;
           std::string Typ;
           int irgendwas;
           float money;
    };
    

    ein objekt dieser klasse kannst du NICHT per write() einfach so schreiben.

    std::ostream& operator<<(std::ostream &out, const sPlayer &my_player)
    {
       /* wie du einen string speicherst bleibt dir ueberlassen,
          das ist nur ein beispiel wie du es machen koenntest */
       out.write(my_player.name.c_str(), my_player.size() + 1);
       out.write(my_player.Typ.c_str(), my_player.size() + 1);
       out.write(reinterpret_cast<const char*>(&(my_player.irgendwas)), sizeof(my_player.irgendwas));
       out.write(reinterpret_cast<const char*>(&(my_player.money)), sizeof(my_player.money));
      return out; 
    }
    

    sizeof(std::string) gibt dir schliesslich auch immer die gleiche groesse zurueck, egal wie lange der string auch sein mag.

    auslesen musst es dann natuerlich auch wieder einzeln. kannst nichtauf einen schwung mit read() machen.

    Meep Meep



  • Ohh haa jetzt hab ich sehr viele Compile errors :

    nfin.read(wPlayer.name.c_str(), wPlayer.size() + 1);
    

    --> class sPlayer has no member named size

    nfin.read(reinterpret_cast<const char*>(&(wPlayer.health)), sizeof(wPlayer.health));
    

    ---> invaild conversion const char* to char*

    nfin.read(reinterpret_cast<const char*>(&(wPlayer.health)), sizeof(wPlayer.health));
    

    -----> initializing argument 1 of ´std::basic_istream<_charT, _Traits>& std::basic_istream<_CharT,_traits>read(_ChartT*, std::streamsize) (with _CHarT =char, _Traits =std::char_traits<char>]

    nfout.write(wPlayer.name.c_str(), wPlayer.size() + 1);
    

    ----> request for member ´name in wPlayer´, which is of non-class type ´sPlayer*´
    (das mit allen immer durch ...)

    Hat mich erst ma geschockt als so viele Fehler kamen xD .. liegt an der übergabe von (sPlayer *wPlayer) oder ? wie muss ich das denn richtig machen ?



  • hab auch falsch geschrieben.

    sollte

    std::ostream& operator<<(std::ostream &out, const sPlayer &my_player)
    {
       /* wie du einen string speicherst bleibt dir ueberlassen,
          das ist nur ein beispiel wie du es machen koenntest */
       out.write(my_player.name.c_str(), my_player.name.size() + 1);
       out.write(my_player.Typ.c_str(), my_player.Typ.size() + 1);
       out.write(reinterpret_cast<const char*>(&(my_player.irgendwas)), sizeof(my_player.irgendwas));
       out.write(reinterpret_cast<const char*>(&(my_player.money)), sizeof(my_player.money));
      return out;
    }
    

    fuer den read brauchst du auch nor einen normalen char* - cast und keinen const char* - cast. den solltest nur beim write machen.

    ich glaub dir fehlen einige grundlagen

    Meep Meep

    Meep Meep



  • mh...jetzt mekkert er noch über die strings beim einlesen

    nfin.read(wPlayer.name.c_str(), wPlayer.name.size() + 1);
    

    --> invaild conversion const char* to char*

    und über alle wirtes:

    nfout.write(reinterpret_cast<const char*>(&(wPlayer.money)), sizeof(wPlayer.money));
    

    da sagt er : --> request for member ´money´ in wPlayer´, which is of non-class type ´sPlayer*´

    Und das sagt er bei allen writes .. sonst hat er keine Fehler mehr .. außer das ich paar falsche aufrufe hab aber das bekomm ich sicherlich alleine hin ,..
    ich weiß das mir paar Grundlagen fehlen ich bin noch am lernen von Ifstream
    und danach möchte ich mich mit der Netzwerk Programmierung außeinander setzen .
    Ich hab irgendwie voll Probleme das save and load in den Hauptquellcode einzubaun , weil als ich das ganze unabhänig geschrieben hatte, hat alles geklappt.
    siehe hier :

    class sAccount      //richtige Klasse anstatt Struct
    {
    public:
            string name;
            string HeroTyp;
            int health;
            int mana;
            int maxdmg;
            int mindmg;
            int defence;
    };
    
    int main()
    {
    
        sAccount a1, a2;   //zwei Instanzen werden erzeugt
    
        a1.name = "Toa";
        a1.HeroTyp = "Mage";
        a1.health = 10;
        a1.mana = 4;
        a1.maxdmg = 12;
        a1.mindmg = 2;
        a1.defence = 99;
    
       int eing;  //menü zum auswählen ...
       cout <<"1 --> Speichern"<<"\n";
       cout <<"2 --> Laden"<<"\n";
       cout <<"ihre Wahl:";
       cin >> eing;
       switch(eing)
       {
    
      case 1 :   //Save !!
      {
       system("cls");
       cout << "Waehle einen Accountnamen:" << flush;  // unter welchem namen soll es gespeichert werden
       string file_name;
       cin >> file_name;
       CreateDirectory("Saves", NULL);   //legt ordner save an
       file_name.insert(0, "Saves\\");
       if (file_name.substr(file_name.length() - 4) != ".sav")  //Hängt an den namen ein Sav
       file_name += ".sav";
    
       ofstream nfout(file_name.c_str(), ios::binary );   //Den inhalt des files kann ein Mensch nicht lesen, i.e. binary, not text format.
       nfout.write((char*)(&a1),sizeof(a1));  //liest die klasse aus und schreibt sie in die Sav file
       nfout.close();  //schließt den Stream
       system("cls");
       cout << "Dein Account wurde  unter '" << file_name << "' gespeichert ..." << endl;
    
      }
      break;
    
      case 2 :  //load !!
    
       system("cls");
       string dateiname;
    
        DIR *dirHandle;   // --- > liestet das verzeichniss Save auf
        struct dirent * dirEntry;
    
        dirHandle = opendir("Saves\\");
        if (dirHandle) {
        while (0 != (dirEntry = readdir(dirHandle))) {
        puts(dirEntry->d_name);
        }
        closedir(dirHandle);
        }
        cout << "Gib den Namen deines Saves ein, den du laden willst:";
        cin >> dateiname;
        dateiname += ".sav";    // wenn man namen ohne sav eingibt hängt das save dran
        system("Cls");
        cout << "Load '" << dateiname << "' ...\n";
        dateiname.insert(0, "Saves\\");
    
        ifstream nfin (dateiname.c_str(), ios::binary);
        nfin.read((char*)(&a2), sizeof(a2));    //lad alles in die Klasse
        nfin.close();  //schließt den STream
    
        // Ausgabe
        cout << "Account as read from binary file " <<dateiname <<endl;
        cout << a2.name << endl;
        cout << a2.HeroTyp << endl;  //gibt alles aus
        cout << a2.health << endl;
        cout << a2.mana << endl;
        cout << a2.maxdmg << endl;
        cout << a2.mindmg << endl;
        cout << a2.defence << endl;
    
        break;
       }
    }
    


  • Toa schrieb:

    mh...jetzt mekkert er noch über die strings beim einlesen

    nfin.read(wPlayer.name.c_str(), wPlayer.name.size() + 1);
    

    --> invaild conversion const char* to char*

    naja die member-funktion c_str() liefert dir ja uach einen 'const char*' zurueck. da kannst du auch nichts reinschreiben. und wPlayer.name kann auch nicht erraten wie lange der string sein wird. das funktioniert so alles nicht. du solltest dich mal gruendlich mit den grundlagen auseinander setzen.

    Toa schrieb:

    nfout.write(reinterpret_cast<const char*>(&(wPlayer.money)), sizeof(wPlayer.money));
    

    da sagt er : --> request for member ´money´ in wPlayer´, which is of non-class type ´sPlayer*´

    seh ich in deinem listing auch nicht wo das sein sollte.
    vielleicht solltest du mal mit volkards kurs anfangen.
    http://www.volkard.de/vcppkold/inhalt.html
    ein gut geschriebener und leichtverstaendlicher kurs.

    Meep Meep



  • also das obere hab ich gelöst . war ja dumm von mir du hast ja ganz recht das c_str() gibt immer ein const char zurück und kann nicht modifiziert werden.

    Aber das untere macht mir Probleme

    nfout.write(reinterpret_cast<const char*>(&(wPlayer.money)), sizeof(wPlayer.money));
    

    ---> request for member ´money´ in wPlayer´, which is of non-class type ´sPlayer*´

    Kann es vielleicht daran liegen das ich der FUnktion die sachen so übergeben hab :

    void cWorld::status_save(sPlayer *wPlayer) <-- also das der SternOperator falsch ist ?



  • Toa schrieb:

    Kann es vielleicht daran liegen das ich der FUnktion die sachen so übergeben hab :

    void cWorld::status_save(sPlayer *wPlayer) <-- also das der SternOperator falsch ist ?

    ja. du uebergibst einen zeiger.
    mach es per referenz oder du musst den zeiger dereferenzieren.

    Meep Meep



  • eine dereferenzierung würde dann so ausehen sPlayer&*wPlayer; oder ? .. ach man so kurz vor dem Ziel xD



  • ne so wPlayer->money. Besser wäre es aber wenn du keinen zeiger sondern eine referenz übergen würdest. also status_save(sPlayer& wPlayer). Beim Aufrufen der funktion dann ohne & übergeben.



  • Hab jetzt zwar keine Compile errors mehr aber es geht immer noch net *heul* ich verzeifel noch xD

    Meine Klasse :

    class sPlayer
    {
          public:
           std::string name;
           std::string HeroTyp;
           int health;
           int mana;
           int maxdmg;
           int mindmg;
           int defence;
           int level;
           int xp;
           float geld;
    };
    

    Die Klasse in der die Funktionen Deffiniert sind :

    class cWorld
    {
          public:
    
                   // Kirche - save /Delete Account / SpielZeit anzeige
                 void Kirche(sPlayer *wPlayer);
                 void LoadPlayer();
    
                 void status_save(sPlayer& wPlayer);
    
    };
    

    die FUnktion in der ich die Kirche aufrufe in der man Saven kann :

    void cWorld::Greenvillage(sPlayer *wPlayer)
    {
    
         string eingabe;
         // Auswahl
         system("cls");
         getline(20); colorcout('1',"Wohin moechten Sie?"); getline(20);
         cout
              << "-Kirche\n";
         newline(1); // hehe :P
    
         cin >> eingabe;
    
    ..... Bla Bla ..: 
    
         else if(eingabe == "-Kirche" ||eingabe == "-Kirche" ||eingabe == "kirche" ||eingabe == "Kirche")
             Kirche(wPlayer); //Kirche - speichern - löschen - spielzeit
         {
    
    }
    

    DIe Kirche an SIch in der man speichern kann:

    void cWorld::Kirche(sPlayer *wPlayer)     //Kirche - speichern - löschen - spielzeit
    {
        char menue;
    
    	cout << "(s)pielstand speichern\n";
    	cout << "(a)ccounts löschen";
    	cout << "(g)esamte Spielzeit anzeigen";
    	cout << "____________________\n";
    	cout << "Bitte w\204hlen:\n";
    	cin >> menue;
    	cout << "\a";
    	switch (menue)
    	{
    		case ('s'):
    			{
    				cWorld save;
                    save.status_save(*wPlayer);  // das ist nur WIchtig !!!!
    			}break;
    		case ('a'):
    			{
    				cout << "loeschen";
    			}break;
    		case ('g'):
    			{
    				cout << "spielzeit";  //Muss noch gemacht werden !
    
    			}break;
    
    	}
    	}
    

    Das Saven:

    void cWorld::status_save(sPlayer& wPlayer)
    {
    
       cout << "Waehle einen Accountnamen:" << flush;  // unter welchem namen soll es gespeichert werden
       string file_name;
       cin >> file_name;
       CreateDirectory("Saves", NULL);   //legt ordner save an
       file_name.insert(0, "Saves\\");
       if (file_name.substr(file_name.length() - 4) != ".sav")  //Hängt an den namen ein Sav
       file_name += ".sav";
    
       ofstream nfout(file_name.c_str(), ios::binary );   //Den inhalt des files kann ein Mensch nicht lesen, i.e. binary, not text format.
       nfout.write(wPlayer.name.c_str(), wPlayer.name.size() + 1);
       nfout.write(wPlayer.HeroTyp.c_str(), wPlayer.HeroTyp.size() + 1);   //liest strings ein
       nfout.write(reinterpret_cast<const char*>(&(wPlayer.health)), sizeof(wPlayer.health));   //liest alles einzeln ein
       nfout.write(reinterpret_cast<const char*>(&(wPlayer.mana)), sizeof(wPlayer.mana));
       nfout.write(reinterpret_cast<const char*>(&(wPlayer.maxdmg)), sizeof(wPlayer.maxdmg));
       nfout.write(reinterpret_cast<const char*>(&(wPlayer.mindmg)), sizeof(wPlayer.mindmg));
       nfout.write(reinterpret_cast<const char*>(&(wPlayer.defence)), sizeof(wPlayer.defence));
       nfout.write(reinterpret_cast<const char*>(&(wPlayer.level)), sizeof(wPlayer.level));
       nfout.write(reinterpret_cast<const char*>(&(wPlayer.xp)), sizeof(wPlayer.xp));
       nfout.write(reinterpret_cast<const char*>(&(wPlayer.geld)), sizeof(wPlayer.geld));
       nfout.close();  //schließt den Stream
       system("cls");
       cout << "Dein Account wurde  unter '" << file_name << "' gespeichert ...\n\n" ;
       system("pause");
    
    }
    

    Und Später dann das laden und ausgeben :

    void cWorld::LoadPlayer()
    {
        sPlayer  wPlayer;
    
       string dateiname;
    
        DIR *dirHandle;   // --- > liestet das verzeichniss Save auf
        struct dirent * dirEntry;
    
        dirHandle = opendir("Saves\\");
        if (dirHandle) {
        while (0 != (dirEntry = readdir(dirHandle))) {
        puts(dirEntry->d_name);
        }
        closedir(dirHandle);
        }
        cout << "Gib den Namen deines Saves ein, den du laden willst:";
        cin >> dateiname;
        dateiname += ".sav";    // wenn man namen ohne sav eingibt hängt das save dran
        system("Cls");
        cout << "Load '" << dateiname << "' ...\n";
        dateiname.insert(0, "Saves\\");
    
        ifstream nfin (dateiname.c_str(), ios::binary);
    
        nfin.read(reinterpret_cast< char*>(&(wPlayer.name)), sizeof(wPlayer.name));
        nfin.read(reinterpret_cast< char*>(&(wPlayer.HeroTyp)), sizeof(wPlayer.HeroTyp));
        nfin.read(reinterpret_cast< char*>(&(wPlayer.health)), sizeof(wPlayer.health));   //liest alles einzeln ein
        nfin.read(reinterpret_cast< char*>(&(wPlayer.mana)), sizeof(wPlayer.mana));
        nfin.read(reinterpret_cast< char*>(&(wPlayer.maxdmg)), sizeof(wPlayer.maxdmg));
        nfin.read(reinterpret_cast< char*>(&(wPlayer.mindmg)), sizeof(wPlayer.mindmg));
        nfin.read(reinterpret_cast< char*>(&(wPlayer.defence)), sizeof(wPlayer.defence));
        nfin.read(reinterpret_cast< char*>(&(wPlayer.level)), sizeof(wPlayer.level));
        nfin.read(reinterpret_cast< char*>(&(wPlayer.xp)), sizeof(wPlayer.xp));
        nfin.read(reinterpret_cast< char*>(&(wPlayer.geld)), sizeof(wPlayer.geld));
        nfin.close();  //schließt den STream
    
        cout << "Account as read from binary file " <<dateiname <<endl;
        system("pause");
        cout << wPlayer.name << endl;
        system("pause");
        cout << wPlayer.HeroTyp << endl;
        system("pause");
        cout << wPlayer.health << endl;
        system("pause");
        cout << wPlayer.mana << endl;
        system("pause");
        cout << wPlayer.maxdmg << endl;
        system("pause");
        cout << wPlayer.mindmg << endl;
        system("pause");
        cout << wPlayer.defence << endl;
        system("pause");
        cout << wPlayer.xp << endl;
        system("pause");
        cout << wPlayer.geld << endl;
        system("pause");
    
    }
    

    ...SO jetzt hab ich echt alles gepostet , ich bin am rande der verzeiflung ..Seit Stunden und Tagen verusche ich es einzubaun und es geht einfach nicht

    MFG TOa



  • Hallo zusammen,

    bietet boost nicht Mittel zur Serialisierung?

    Grüße Martin



  • http://www.boost.org/libs/serialization/doc/index.html

    sollte man da einfach mal einsetzen



  • nfin.read(reinterpret_cast< char*>(&(wPlayer.name)), sizeof(wPlayer.name));
    

    So geht das nicht. Man kann nicht einfach einen std::string* nach char* casten.

    Mach's z.B. so:

    std::vector<char> buf(1024);
    nfin.read(&(buf[0]), buf.size());
    wPlayer.name.assign(buf.begin(), buf.end());
    

    Damit würden allerdings nur Strings bis 1024 Zeichen eingelesen werden können, außerdem hab ich's jetzt nicht getestet.



  • Am besten schreibst du vor jeden String noch einmal die Länge des strings. Denn mein Beispiel würde jetzt nur mit Strings gehen, die immer 1024 Zeichen lang sind.



  • bitte keine tipps mehr über unsichere cast-orgien - gerade anfänger haben damit arge probleme

    das boost zeug sollte wesentlich praktischer und einfacher zu verwenden sein



  • das problem beim einlesen seiner strings ist, das er nicht weiß wie lange sie sind. also koennte man beim speichern der strings noch die laenge mitgeben. dann hat man schon ein problem weniger.

    inline void WriteStringToStream(std::ostream &out, const std::string &str)
    {
       std::size_t len = str.size();
       out.write(reinterpret_cast<const char*>(&len), sizeof(len));
       out.write(str.c_str(), len); // '\0' brauchen wir nicht mehr, weil wir die laenge speichern
    }
    

    zum auslesen kann man dann folgendes machen:

    void ReadStringFromStream(std::istream &in, std::string &str)
    {
       std::size_t len;
       in.read(reinterpret_cast<char*>(&len), sizeof(len));
       char *buffer = new char[len];
       in.read(buffer, len);
       str.assign(buffer, len);
       delete[] buffer;
    }
    

Anmelden zum Antworten