exception bei string = string warum????



  • Hast du auch MAX_KEYS g_keys?
    Sonst könnte es sein, dass in der Add-Funktion ein Überlauf passiert.
    Du aber erst mit der Auslagerung des Strings im Heap einen Fehler bekommst!

    Gruß
    tHOMY



  • Hm ich kann im Moment nichts erkennen was auf den ersten Blick den Fehler auslösen könnte. Allerdings gibts doch einige Anmerkungen zu machen:

    1. Du hast ein using namespace std im Header stehen. Merke: Benutze NIE ein using namespace xy in einem Header. Denn jede Datei die diesen Header einbindet bindet damit die using-Direktive ein, was unter Umständen die Semantik des Codes ändern kann. Using namespace in .cpp-Dateien ist ok, wenn die using-Direktive nach allen includes steht.

    2. Viele deiner Funktionen haben einen std::string als Argument. Wenn du dieses Argument in der Funktion noch veränderst ist das evtl. ok, aber meistens vergleicht man es ja nur oder gibts an ander Funktionen weiter (die damit auch nichts anstellen), und in dem Fall nimmt mann eine const Referenz auf den String und spart somit beim Funktionsaufruf die Kopie des Strings.

    Key::Key(string const& sName, string const& sKey)
    
    1. Das Interface deiner Key-Klasse ist leicht überladen. Zum Beispiel hat sie neben den Gettern noch eine Vergleichsmethode für die einzelnen Elemente, die eigentlich unnötig ist. Folgendes ist äquivalent:
    std::string xy;
    Key k;
    k.IsName(xy); //ist der name des Keys gleich xy?
    k.GetName() == xy; //macht das selbe ohne die zusätzliche Methode
    

    Man muss immer abwägen zwischen einem zu fetten Interface und der Bequemlichekeit bei der Benutzung.
    Ein weiteres Beispiel sind der operator! und die beiden Vergleichsoperatoren mit bool. Alle drei machen im Grunde das selbe. Da wäre es evtl. angebracht, eine konvertierung nach bool zu implementieren, die die gesamte Funktionalität auf einen Griff liefert. Google evtl. mal nach "safe bool idiom"



  • Also deine Art und Weise, die std::strings zu speichern und zu laden ist bestimmt nicht im Sinne des Herren:

    ofDatei.write((char *) &m_sName, sizeof(m_sName));
    ofDatei.write((char *) &m_sKey, sizeof(m_sKey));
    
    ifDatei.read((char *) &m_sName, sizeof(m_sName));
    ifDatei.read((char *) &m_sKey, sizeof(m_sKey));
    

    Was auch immer da passiert, evtl. stürzt es nicht direkt ab, aber du hast dir da ein riesen Problem eingehandelt.
    An der Addresse von einem std::string wird erstmal nur ein int mit der Länge des Strings und ein Pointer auf den wahren String zu finden sein und evtl. halt noch mehr organisatorische Daten, der String selber auf jeden Fall nicht. Mit sizeof(std::string) wirst du damit dann auch nur die Größe der organisatorischen Daten bekommen. Wenn du strings in binärer Form rausschreiben willst (also auch nicht-lesbare Zeichen und mehrere 0's), musst du schon etwas mehr aufwand treiben.

    zB:

    void writestring_binary( std::ostream& o, std::string str ) {
        std::size_t temp = str.size();
        o.write( (const char*) &temp, sizeof( std::size_t ) );
        o.write( str.data(), str.size() );
    }
    
    void readstring_binary( std::istream& i, std::string str ) {
        std::size_t s;
        buf.read( (char*)&s, sizeof( std::size_t ) );
    
        str.clear();
        str.reserve( s );
        char c;
        for( std::size_t i=0; i<size; ++i ) {
            i.get( c );
            str += c;
        }
    }
    

    PS: Da dynamisch Speicher allozieren war bestimmt nicht die beste Idee, hab das mal ge-updatet.
    PPS: Das int kann man natürlich in dem binären fall auch nicht mit operator<< bzw. >> schreiben, weil der ja auf leerzeichen hofft.



  • Ist das so korrekt?

    o.write( (const char*) &str.size(), sizeof( std::size_t ) );
    

    Was liefert denn &str.size() eigentlich? Einen Zeiger auf die Funktion, einen Zeiger auf eine temporäre Variable oder (falls inline) einen Zeiger auf eine interne Membervariable von string?
    Mal abgesehen davon, dass string::size() den Typ std::string::size_type zurückliefert (sollte aber identisch mit size_t sein).



  • Ich hatte jetzt gehofft, dass er die addresse der temporären (rvalue)-Variable nimmt, hab das aber nicht kompiliert. Schließlich kann man doch auch const-Referenzen so übergeben, wenn ich mich recht entsinne?
    Edit: Okay, geht nicht (Wird lvalue benötigt), warum auch immer, der Wert ist doch zumindest da? Ich habe es im Code oben korrigiert.



  • Braunstein schrieb:

    Ist das so korrekt?

    o.write( (const char*) &str.size(), sizeof( std::size_t ) );
    

    Was liefert denn &str.size() eigentlich? Einen Zeiger auf die Funktion, einen Zeiger auf eine temporäre Variable oder (falls inline) einen Zeiger auf eine interne Membervariable von string?
    Mal abgesehen davon, dass string::size() den Typ std::string::size_type zurückliefert (sollte aber identisch mit size_t sein).

    Sofern std::basic_string<>::size() nicht eine Referenz auf irgendwas Internes zurückgibt (und das tut es nicht), liefert es Dir undefiniertes Verhalten. Egal, ob inline oder nicht.



  • Decimad schrieb:

    Schließlich kann man doch auch const-Referenzen so übergeben, wenn ich mich recht entsinne?

    Jein. Der einzige trick, ein temporäres Objekt etwas länger am Leben zu halten ist, es an eine const Referenz zu binden:

    Foo bar();
    
    Foo const& f = bar(); //das temporäre Ergebnis von bar wird an f gebunden
    Foo & f2 = bar(); //undefiniert, da f2 keine const Referenz ist.
    


  • pumuckl schrieb:

    Foo f2 = bar(); //undefiniert, da f2 keine const Referenz ist.
    

    Da fehlt ein '&'.



  • jup, danke, erledigt 🙂



  • Erstmal vielen dank für alle eure Verbesserungsvorschläge!!!

    Ich habe den Code etwas verändert, und der Fehler ist weg, das einzige was ich gemacht habe war dass ich die save und load funktion verändert habe und der fehler ist weg...

    also das war ein sehr seltsamer fehler.

    Leider bleibt ein Fehler: Wenn ich einen Key eingebe und speichere, dann das Prog neu starte, dann ist der Key weg, also wurde er nicht gespeichert... warum????

    hier nochmal der aktuelle code:

    Key.h:

    #ifndef KEY
    #define KEY
    
    #include <string>
    #include <fstream> 
    
    class Key
    {
      public:
      Key() {Delete();}
      Key(std::string const& sName, std::string const& sKey) : m_bExist(false), m_sName(sName), m_sKey(sKey) {}
      ~Key() {}
    
      void Delete();
    
      void SetName(std::string const& sName = "");
      void SetKey(std::string const& sKey = "");
    
      std::string GetName() const;
      std::string GetKey() const;
    
      bool IsName(std::string const&);
      bool IsKey(std::string const&);
    
      bool Save(std::string const& sFile = "Keys.dat");
      bool Load(std::string const& sFile = "Keys.dat");
    
      operator bool();
    
      private:
      void readstring_binary(std::istream&,std::string);
      void writestring_binary(std::ostream&,std::string);
    
      bool m_bExist;
      std::string m_sName;
      std::string m_sKey;
    };
    
    #endif
    

    Key.cpp

    #include "key.h"
    
    using namespace std;
    
    void Key::Delete()
    {
      m_bExist = false;
      m_sName = "";
      m_sKey = "";
    }
    
    void Key::SetName(string const& sName)
    {
      m_bExist = true;
      m_sName = sName;
    }
    
    void Key::SetKey(string const& sKey)
    {
      m_bExist = true;
      m_sKey = sKey;
    }
    
    string Key::GetName() const
    {
      return m_sName;
    }
    
    string Key::GetKey() const
    {
      return m_sKey;
    }
    
    bool Key::IsName(string const& sName)
    {
      return m_sName == sName;
    }
    
    bool Key::IsKey(string const& sKey)
    {
      return m_sKey == sKey;
    }
    
    bool Key::Save(string const& sFile)
    {
      bool bGood;
      ofstream ofDatei(sFile.c_str(), ios::binary | ios::trunc);
    
      ofDatei.write((char *) &m_bExist, sizeof(bool));
      writestring_binary(ofDatei, m_sName);
      writestring_binary(ofDatei, m_sKey);
    
      bGood = ofDatei.good();
    
      ofDatei.close();
      ofDatei.clear();
    
      return bGood;
    }
    
    bool Key::Load(string const& sFile)
    {
      bool bGood;
      ifstream ifDatei(sFile.c_str(), ios::binary);
    
      ifDatei.read((char *) &m_bExist, sizeof(bool));
      readstring_binary(ifDatei, m_sName);
      readstring_binary(ifDatei, m_sKey);
    
      bGood = ifDatei.good();
    
      ifDatei.close();
      ifDatei.clear();
    
      return bGood;
    }
    
    Key::operator bool()
    {
      return m_bExist;
    }
    
    void Key::writestring_binary( std::ostream& o, std::string str )
    {
        std::size_t temp = str.size();
        o.write( (const char*) &temp, sizeof( std::size_t ) );
        o.write( str.data(), str.size() );
    }
    
    void Key::readstring_binary( std::istream& i, std::string str )
    {
        std::size_t s;
        i.read( (char*)&s, sizeof( std::size_t ) );
    
        str.clear();
        str.reserve( s );
        char c;
        for( std::size_t n = 0; n < s; ++n )
        {
            i.get( c );
            str += c;
        }
    }
    

    und die funktion, die ich zum speichern/laden aufrufe + die relewanten deklarationen

    // Globale variablen
    Key      g_Keys[MAX_KEYS];    // MAX_KEYS ist zur zeit 126
    
    // die funktionen
    int load()
    {
      for(int i = 0; i < MAX_KEYS; i++)
      {
        if(!g_Keys[i].Load("data\\Keys.dat"))
          return ERROR_READ;
      }
    
      return OK;
    }
    
    int save()
    {
      for(int i = 0; i < MAX_KEYS; i++)
      {
        if(!g_Keys[i].Save("data\\Keys.dat"))
          return ERROR_WRITE;
      }
    
      return OK;
    }
    

    achja und ne kleine frage am rande was ist foo und was ist bar über das ihr hier redet?



  • Yazoo schrieb:

    also das war ein sehr seltsamer fehler.

    Eigentlich nicht. Ein std::string allokiert dynamisch Speicher für die Zeichenkette, die er repräsentiert. Wenn du nur das Objekt selbst so wie gehabt in seiner Binärdarstellung in eine Datei schreibst, schreibst du nur den Pointer den der String intern benutzt in die Datei, nicht aber den Inhalt des dynamisch allokierten Speichers. Beim Lesen aus der Datei bastelst du dann die binäre Repräsentation eines Strings zusammen, der schon lang nichtmehr existiert und der vor allem schon seinen Speicher freigegeben hat. Damit gehört dieser Speicher längst jemand anderem, und trotzdem hat der string-Zombie aus der Datei noch einen Pointer auf den Speicher. Sobald er drauf zugreift krachts.

    Was deinen aktuellen Code angeht: wieder etwas zu viel des Guten (die getter, setter, vergleiche usw. hätten wir nicht nochmal gebraucht), dafür fehlt das wichtigste, nämlich die main()-Funktion



  • die main ist ganz schön groß aber nagut:

    #include "key verwalter.h"
    
    Key      g_Keys[MAX_KEYS];
    bool     g_baHits[MAX_KEYS];
    
    int main()
    {
      bool bError = false;
      string sInput;
      string sChildInput;
    
      if(load() == ERROR_READ)
        cout << "FEHLER: Die Datei data\\Keys.dat konnte nicht gelesen werden!\n\n";
    
      string valid_inputs[]={"exit", "e", "add", "delete", "del", "search", "mod", "cls", "help", "h"};
      enum valid_inputs_words{exit, e, addd, delee, del, searchh, modd, clss, helpp, h};
    
      int valid_count = sizeof(valid_inputs)/sizeof(string);
    
      while(sInput != "exit" && sInput != "e")
      {
        anfang:
        do
        {
          cout << "Befehl> ";
          CIN.Read(); 
    
          if(CIN.GetArgError(0) != ADVCIN_OK)
          {
            bError = true;
            cout << "FEHLER: Fehlerhafte Eingabe!\n\n";
          }
          else
          {
            CIN.LowerArg(0);
            sInput = CIN.GetArgString(0);
            bError = false;
          }
        }while(bError == true);
    
        switch (find(valid_inputs, valid_inputs + valid_count, sInput) - valid_inputs)
        {
          case addd:
          {
            if(CIN.GetArgError(1) != ADVCIN_ERROR_NO_ARG && CIN.GetArgError(2) != ADVCIN_ERROR_NO_ARG)
            {
              if(add(CIN.GetArgString(1), CIN.GetArgString(2)) == ERROR_EXISTS)
                cout << "FEHLER: Der Key " << CIN.GetArgString(1) << " Existiert bereits!\n\n";
              else
                cout << "Der Key wurde erfolgreich hinzugefuegt.\n\n";
    
              if(save() == ERROR_WRITE)
                cout << "FEHLER: Es konnte nicht in die Datei data\\Keys.dat geschrieben werden!\n\n";
            }
            else
              cout << "FEHLER: Falsche Parameter!\n\n";
    
            break;
          }
    
          case delee:
          case del:
          {
            if(CIN.GetArgError(1) != ADVCIN_ERROR_NO_ARG)
            {
              cout << "WARNUNG: Sind sie sicher, dass sie den Key " << CIN.GetArgString(1) << " loeschen wollen? (J/N) > ";
              cin >> sChildInput;
    
              for(unsigned int i = 0; i < sChildInput.length(); ++i)
                sChildInput[i] = tolower(sChildInput[i]);
    
              if(sChildInput == "j" || sChildInput == "ja")
                if(dele(CIN.GetArgString(1)) == ERROR_NOT_FOUND)
                  cout << "FEHLER: Der Key " << CIN.GetArgString(1) << " existiert nicht!\n\n";
                else
                  cout << "Der Key wurde erfolgreich geloescht.\n\n";
    
              if(save() == ERROR_WRITE)
                cout << "FEHLER: Es konnte nicht in die Datei data\\Keys.dat geschrieben werden!\n\n";
            }
            else
              cout << "FEHLER: Falscher Parameter!\n\n";
    
            break;
          }
    
          case searchh:
          {
            if(CIN.GetArgError(1) != ADVCIN_ERROR_NO_ARG)
            {
              search(CIN.GetArgString(1));
              if(!found(g_baHits))
              {
                cout << "FEHLER: Der Key " << CIN.GetArgString(1) << " ist nicht vorhanden!\n\n";
              }
              else
              {
                for(int i = 0; i < MAX_KEYS; i++)
                {
                  if(g_baHits[i] == true)
                  {
                    cout << "Name:  " << g_Keys[i].GetName() << "\n";
                    cout << "Key:   " << g_Keys[i].GetKey() << "\n\n";
                  }
                }
              }
            }
            else
              cout << "FEHLER: Falscher Parameter!\n\n";
    
            break;
          }
    
          case clss:
          {
            cls();
            break;
          }
    
          case modd:
          {
            if(CIN.GetArgError(1) != ADVCIN_ERROR_NO_ARG && CIN.GetArgError(2) != ADVCIN_ERROR_NO_ARG && CIN.GetArgError(3) != ADVCIN_ERROR_NO_ARG)
            {
              if(mod(CIN.GetArgString(1), CIN.GetArgString(2), CIN.GetArgString(3)) == ERROR_NOT_FOUND)
                cout << "FEHLER: Der Key " << CIN.GetArgString(1) << " ist nicht vorhanden!\n\n";
              else
                cout << "Der Key wurde erfolgreich geaendert.\n\n";
    
              if(save() == ERROR_WRITE)
                cout << "FEHLER: Es konnte nicht in die Datei data\\Keys.dat geschrieben werden!\n\n";
            }
            else
              cout << "FEHLER: Falsche Parameter!\n\n";
    
            break;
          }
    
          case helpp:
          case h:
          {
            help();
            break;
          }
    
          case exit:
          case e:
          {
            break;
          }
    
          default:
          {
            cout << "\nFEHLER: Unbekannter Befehl: " << sInput << "\n\n";
          }
        }
      }
    
      if(save() == ERROR_WRITE)
      {
        cout << "FEHLER: Es konnte nicht in die Datei data\\Keys.dat geschrieben werden!\n";
        cout << "Wollen sie trotzdem beenden? (J/N) > ";
    
        cin >> sInput;
    
        if(sInput != "j" || sInput == "ja")
          goto anfang;                           // sry für die eine Spaghetti ich war zu faul noch ne schleife einzubauen
      }  
    
      return 0;
    }
    

    ich denke dann sollte ich euch auch mal über meine advcin klasse invormieren.

    Ich denke die Definition reicht:

    class advcin
    {
      public:
      advcin();
      ~advcin();
    
      void Read(bool bLower = false, int iNumArgs = 0);   // Bei iNumArgs = 0 gibt es keine beschränkung außer die Länge der m_saArg
    
      std::string GetArgString(int) const;
      int GetArgError(int) const;
    
      void LowerArg(int iArg = -1);
    
      private:
      void Sort(int);
      void Clear();
    
      std::string m_sAll;
      std::string m_saArg[MAX_ARGS];
    };
    


  • Alter Verwalter! Lesbar ist etwas anderes. Lösche doch am besten die Teile, die mit dem Problem nix zu tun haben aus deinen Code(riesen)schnipseln. Gibt es das Wort "Schnips"? hehe
    Das letzte Posting mag ich mir echt nicht durchlesen... Aber in dem davor sah es so aus, dass du in der Schleife immer die gleiche Datei öffnest, das erste Key-Value paar ausliest, die Datei schließt, wieder aufmachst, das erste Key-Value paar ausliest usw. usf. Das wolltest du so bestimmt nicht.



  • Decimad schrieb:

    Alter Verwalter! Lesbar ist etwas anderes. Lösche doch am besten die Teile, die mit dem Problem nix zu tun haben aus deinen Code(riesen)schnipseln. Gibt es das Wort "Schnips"? hehe
    Das letzte Posting mag ich mir echt nicht durchlesen... Aber in dem davor sah es so aus, dass du in der Schleife immer die gleiche Datei öffnest, das erste Key-Value paar ausliest, die Datei schließt, wieder aufmachst, das erste Key-Value paar ausliest usw. usf. Das wolltest du so bestimmt nicht.

    ............................................................ leider gibts hier keinen smilie der sich den kopf vor die wand haut!! also *BUUUMMM Kopf vordie Wand hau BBBBUUUUMMM*

    danke!!!

    ich bin echt ein idiot!!!!!!!!!!



  • so hab die sachen geändert und jetzt funktioniert es auch!!!

    thx an alle die sich die mühe gemacht haben und sich durch meinen code gewühlt haben!!!!!!

    /edit und hier nochmal die geänderten stellen:

    int load()
    {
      ifstream fI("data\\Keys.dat", ios::binary);
    
      for(int i = 0; i < MAX_KEYS; i++)
      {
        if(!g_Keys[i].Load(fI))
          return ERROR_READ;
      }
    
      fI.close();
      fI.clear();
    
      return OK;
    }
    
    int save()
    {
      ofstream fO("data\\Keys.dat", ios::binary | ios::trunc);
    
      for(int i = 0; i < MAX_KEYS; i++)
      {
        if(!g_Keys[i].Save(fO))
          return ERROR_WRITE;
      }
    
      fO.close();
      fO.clear();
    
      return OK;
    }
    
    bool Key::Save(std::ostream const& o)
    {
      bool bGood;
    
      o.write((char *) &m_bExist, sizeof(bool));
      writestring_binary(o, m_sName);
      writestring_binary(o, m_sKey);
    
      bGood = o.good();
    
      return bGood;
    }
    
    bool Key::Load(std::istream const& i)
    {
      bool bGood;
    
      i.read((char *) &m_bExist, sizeof(bool));
      readstring_binary(i, m_sName);
      readstring_binary(i, m_sKey);
    
      bGood = i.good();
    
      return bGood;
    }
    
    void Key::writestring_binary( std::ostream const& o, std::string const& str )
    {
        std::size_t temp = str.size();
        o.write( (const char*) &temp, sizeof( std::size_t ) );
        o.write( str.data(), str.size() );
    }
    
    void Key::readstring_binary( std::istream const& i, std::string& str )
    {
        std::size_t s;
        i.read( (char*)&s, sizeof( std::size_t ) );
    
        str.clear();
        str.reserve( s );
        char c;
        for( std::size_t n = 0; n < s; ++n )
        {
            i.get( c );
            str += c;
        }
    }
    


  • Freut mich 😉
    Achso - zum Thema Foo und Bar:
    http://en.wikipedia.org/wiki/Foo



  • thx


Anmelden zum Antworten