Unbehandelte Ausnahme-Exception wenn man Methode verläßt. (Serialisierung, ofstream, ifstream)
-
Hallo erstmal,
trotz meines ersten Beitrages, hoffe ich, dass mir jemand das folgende Mysterium aufklären kann

Im Zuge des Einf. C++-Vorlesung an der Uni müssen wir ein Projekt abgeben.
Da ich neben dem Studium recht viel Java-Code und C++ für mich eher unangenehm ist, beiße ich mir an der folgenden Exception schon seit Stunden die Zähne aus.Anwendungsfall:
Wir müssen um zur Klausur zugelassen zu werden neben den Übungen ein Projekt abgeben.Eine klassische Studentendatenbank
.Die Datenbank soll in einem Array eine festgelegte Anzahl an Studenten halten.
Diese Datenbank soll in bei Bedarf in einer Datei gespeichert, und auch wieder eingelesen werden können.
Zusätzlich sollte man wählen können, ob man beim Speichern die aktuellen Studenten "appeneded" oder das DB-File überschreibt.
Alles schön und gut, der klassische Ansatz der hier wohl gesehen werden will ist jede Zeile ein Student, Klartext, kommasepariert.
Da wir in der Klausur den ganzen compilierten Krempel händisch abschreiben(!!) möchten, würde ich gerne wenig Code erzeugen und dachte da natürlich an die eingebaute Serialisierungsfunktion ;-).
Ich habe es geschafft, das ganze StudentenArray zu serialisieren und auszulesen. Das war kein Problem. Ich verwende ein zweites bool-Array um freie Plätze im Student-Array zu markieren, selbst das konnte ich hinter dem eigentlichen StudArray binär speichern und auch wieder auslesen. Alles fein.
Doch die Anforderung lautet explizit, man soll an eine gespeicherte DB-Datei die aktuellen Studenten anhängen können.
Also serialisierte ich seit dem jeden Studenten und schrieb diesen in die Datei. Alles gut.
Ich lese nun die Datei solange binär ein, bis ifstream.eof() anschlägt und höre auf. Die Studenten werden erfolgreich gelesen und gespeichert. Doch wenn ich aus der LadeMethode zurück in die main springe kracht es mit folgender Meldung:
Unbehandelte Ausnahme bei 0x1048ad54 (msvcp100d.dll) in Aufgabe2_4.exe: 0xC0000005: Zugriffsverletzung beim Schreiben an Position 0xfeeefeee.Hier der Code zum Speichern, hier läuft alles glatt:
bool StudDatabase::save(std::string fName, bool append) { //oeffne dateistream ofstream outFile; if(append) { outFile.open(fName,ios::binary|ios::app); } else { outFile.open(fName,ios::binary); } if(!outFile) { cout<<fName<<" konnte zum Schreiben nicht geoeffnet werden."<<endl; return false; } //Gehe jeden Studenten durch und schreibe seine Daten in eine Zeile der Datei int i; for(i=0;i<maxCap;i++) { //schaue im bool-Array nach ob der Platz belegt ist if(isFreeIndex[i] == false) //Student gefunden { Student stud = studArray[i]; outFile.write((char*)&stud,sizeof(stud)); } } outFile.close(); return true; }Hier der Code zum Laden, es kracht IMHO explizit beim return.Die Anzahl der in die DB geladenen Datensätze wird korrekt ermittelt und kracht nicht.
short StudDatabase::load(std::string fName) { //Datenhalter "student" Student tmp; //Oeffne Datei ifstream inFile; inFile.open(fName,ios::binary); if(!inFile) { cout<<"Datei konnte nicht geladen werden!"<<endl; return 0; } //Setzt freiePlaetze-Array auf alles frei (zum ueberschreiben) reset(); while(true) { inFile.read((char*)&tmp,sizeof(tmp)); //Pruefe ob wir erfolgreich gelesen haben if(inFile.eof() == true) { //Nein gehe jetzt raus. break; } //fuege hinzu, wenn false kein platz mehr if(addToDB(tmp) == false) { cout<<"Datenbank enthaelt nicht genug Platz um die Datei zu laden. Restliche Datensaetze verfallen"<<endl; } } inFile.close(); //zaehlt anzahl false im freeIndex-Array (quasi Anzahl belegter plaetze) return count(); //es ist egal ob ich count() oder 2 zurück gebe. es kracht im return. }Nun noch die Main mit dem Aufruf der Funktionen:
//includes StudDatabase db; int main(void) { bool exit = false; char t; string tStr; while(!exit) { menue(); cin>>t; switch(t) { //..anderes zeugs case'l':cout<<"Dateiname.?: ";cin>>tStr;cout<<endl;dbLoad(tStr);break; //ruft db.load(string str) auf. case's':cout<<"Dateiname.?: ";cin>>tStr;cout<<endl;dbSave(tStr,false);break; //ruft db.save(tStr,false) auf. case'x':exit=true;;break; default:cout<<"Eingabe nicht erkannt!"<<endl; } //switch ende }//while ende return 0; }Ich frage in der Hoffnung, dass es etwas triviales ist, was ich nicht bedacht habe. Vor Allem in der Load-Funktion.
Notfalls schreibe ich halt tatsächlich einfach Text und parse den selber. Aber es würde mich schon interessieren wo der fehler liegt.
Ich danke jedem schonmal wenn er bis hierhin gelesen hat

€dith:
Hier noch der Auszug aus Student:class Student { private: std::string vorName; std::string nachName; std::string mnr; //matrikelnummer std::string fb; //fachbereich (evtl. mit Buchstaben) std::string sg; //studiengang short sem; //semester public: //... //Destruktor ~Student() { vorName.clear(); nachName.clear(); mnr.clear(); fb.clear(); sg.clear(); //short sem sollte automatisch gelöscht werden ? }Vielen Dank und
viele Grüße,
clayton
-
Hallo clayton!
Willkommen im Forum!
Also Folgendes ist in deinem Code kritisch:
outFile.write((char*)&stud,sizeof(stud));An dieser Stelle schreibst du alle Member des Student-Objektes in deine
Datei. Sobald aber ein Zeiger in deinem Objekt ist, wird der Inhalt
auf den er verweist nicht mitkopiert, sondern nur die Adresse an dem
er sich zur Zeit befindet. Dabei reicht beispielweise schon ein std::string
aus, der intern einen Zeiger auf den Text hat. Ich vermute mal du hast
std::string als Member in deinem Student-Objekt.Wenn du die Daten aus der Datei einließt, steht einfach wieder die Adresse
drin, auf die der Zeiger vorher verwiesen hat. Doch der ursprüngliche
String existiert wahrscheinlich nicht mehr, so dass du auf Speicher verweist,
der nicht zu deinem Programm gehört (-> Zugriffsverletzung).Als zusätzliche Anmerkung:
Aber auch wenn er noch existieren würde, dann würde er beim Zerstören
deines ursprünglichen Objektes und dieser eingelesenen Instanz freigegeben
(doppelte Freigabe -> Problem)Warum kracht es beim Verlassen von load?
Hier wird der Destruktor von Student aufgerufen und der wird versuchen
deinen string(?) freizugeben, der auf den nicht mehr gültigen Speicherbereich
verweist.Wie du siehst mache ich einige Vermutungen bezüglich deiner Student-Klasse.
Ohne Kenntnis deiner Student-Klasse bleibt einem (wohl) nichts anderes übrig,
aber es spricht (denke ich) viel dafür, dass es der Grund ist.
Obwohl ich eigentlich ein Doppel-delete/-free als Fehlermeldung vermutet
hätte. Vielleicht greifst du im Destruktor ja noch auf irgendwelche Daten zu.Hier noch einige Anmerkungen zu deinem Code:
Kuck dir mal Referenzen und const an. Du erstellst ein paar unnötige Kopien:bool StudDatabase::save(const std::string& fName, bool append) short StudDatabase::load(const std::string& fName) Student& stud(studArray[i]);und der bool-Vergleich:
if(inFile.eof()) if(!addToDB(tmp)) if(!isFreeIndex[i])//schaue im bool-Array nach ob der Platz belegt ist
Hier bietet sich wahrscheinlich ein std::vector anbtw: Serialisierst/Speicherst du wirklich den richtigen Studenten?
Gruß,
XSpille
-
Hallo clayton,
die Beschreibung von deinem Projekt hört sich aber nicht unbedingt nach "binärer Serialisierung" an, sondern nach der normalen Text-Serialisierung.
In C++ einfach durch Überladen des '<<' bzw '>>' Streaming-Operators für deine Studentenklasse.
-
XSpille schrieb:
Hallo clayton!
Willkommen im Forum!
Vielen Dank schonmal für den kompetenten Empfang

XSpille schrieb:
Hallo clayton!
Also Folgendes ist in deinem Code kritisch:
outFile.write((char*)&stud,sizeof(stud));An dieser Stelle schreibst du alle Member des Student-Objektes in deine
Datei. Sobald aber ein Zeiger in deinem Objekt ist, wird der Inhalt
auf den er verweist nicht mitkopiert, sondern nur die Adresse an dem
er sich zur Zeit befindet. Dabei reicht beispielweise schon ein std::string
aus, der intern einen Zeiger auf den Text hat. Ich vermute mal du hast
std::string als Member in deinem Student-Objekt.Wenn du die Daten aus der Datei einließt, steht einfach wieder die Adresse
drin, auf die der Zeiger vorher verwiesen hat. Doch der ursprüngliche
String existiert wahrscheinlich nicht mehr, so dass du auf Speicher verweist,
der nicht zu deinem Programm gehört (-> Zugriffsverletzung)....
Warum kracht es beim Verlassen von load?
Hier wird der Destruktor von Student aufgerufen und der wird versuchen
deinen string(?) freizugeben, der auf den nicht mehr gültigen Speicherbereich
verweist.Das ist es vermutlich, es kracht am Ende des Student Destruktors (ja er hat ne Menge Strings ;-))
_String_val() { // destroy the object typename _Alloc::template rebind<_Container_proxy>::other _Alproxy(_Alval); this->_Orphan_all(); //Hier kracht es _Dest_val(_Alproxy, this->_Myproxy); _Alproxy.deallocate(this->_Myproxy, 1); this->_Myproxy = 0; }D.h., das anscheinend erfolgreiche Laden mit korrekter Befüllung war also nur eine glückliche Fügung der Belegung des Speichers (Daten waren noch vorhanden)?
Gibt es eine einfache Möglichkeit die Objekte samt strings zu serialisieren, ohne zusätzliche, externe Bibliotheken zu verwenden ?
Deinen Rat die Suchparameter als const &string zu übergeben ergeben natürlich Sinn. Danke.
Ich denke ich werde zeiteffizienthalber Th69 Ratschlag befolgen und das Schreiben und Parsen (des Textes, kein binary mehr) in den Student auslagern.
Ich danke euch vielmals

Viele Grüße,
clayton
-
Noch ein Tipp, da du wenig Code schreiben möchtest:
//Destruktor ~Student() { vorName.clear(); nachName.clear(); mnr.clear(); fb.clear(); sg.clear(); //short sem sollte automatisch gelöscht werden ? }Der Destruktor ist vollkommen überflüssig, da die std::string selber den Speicher aufräumen.
-
clayton schrieb:
Da wir in der Klausur den ganzen compilierten Krempel händisch abschreiben(!!) möchten, würde ich gerne wenig Code erzeugen und dachte da natürlich an die eingebaute Serialisierungsfunktion ;-).
Es gibt keine eingebaute Serialisierungsfunktion.