Mal wieder ein Strinklassen Problem...
-
So ich habe seit ich das letzte mal hier gepostet habe mich belesen und diverse Dinge überarbeitet... Nur habe ich jetzt einen Fehler, wenn ich dem String erneut einen neuen String zuweise:
#include <iostream> #include <sstream> #include <cstdio> #define Debug(x) cout << x << endl; using namespace std; class String { private: char* data; void copy(char* dest, const char* source) { while (((*dest++) = (*source++))); }; void createMemory(int length) { Debug("New Memory length(" << length << ")") if(length <= 0) length = 1; data = new char[length]; if (data == NULL) throw MemoryException(); if (length > 0) data[length - 1] = '\0'; } public: String() : data(NULL) { createMemory(1); } String(const char* string) : data(NULL) { Debug("Create Memory for charArray("<< string <<")"); createMemory(getSize(string)+1); copy(data, string); Debug("Data: " << data) }; String(String const& other) : data(NULL) { Debug("Create Memory for string("<< other <<")"); createMemory(other.getSize()+1); copy(this->data, other.data); }; ~String() { // Wenn Daten nicht existieren // oder Länge 0 ist, löschen nicht notwendig // Verhindert Memory (double free) corruption // dirty hack... if(data != NULL && getSize() != 0){ delete [] data; } }; int getSize() const { return getSize(data); }; int getSize(const char* ca) const { int i = 0; if (ca != NULL) { for (i = 0; *ca++; i++); } return i; } void append(String string) { // Neue Länge ermitteln int fullLength = getSize() + string.getSize(); // Temporären String erstellen char* tmp = new char[fullLength+1]; // Beide Strings in den temporären string schreiben for (int i = 0; i < getSize(); i++) { tmp[i] = data[i]; } for (int i = 0; i < string.getSize(); i++) { tmp[getSize()+i] = string.data[i]; } // Nullterminator anhängen tmp[fullLength] = '\0'; // Daten löschen und neu initalisieren delete [] data; data = new char[fullLength+1]; // Daten rüber kopieren copy(data, tmp); // Temporären String löschen delete [] tmp; } String operator + (String s) { append(s); return *this; } String operator += (String s) { append(s); return *this; } const char* c_str() const { return data; } operator char * () { if ( data == NULL ) { createMemory(1); } Debug("Convert to charArray: " << data); return data; } operator const char * () const { return c_str(); } int toInteger(){ std::stringstream s(data); int i = 0; s >> i; return i; } }; int main() { String s = "Bla"; s = "123"; cout << s << endl; cout << s.toInteger() << endl; return 0; }Das seltsame es wird alles wunderbar zugewiesen und so weiter und sofort aber wenn ich dann versuche auf die Daten zuzugreifen sind die auf einmal weg? Wo liegt der Fehler ich suche schon seit mehreren Stunden, finde ihn aber einfach nicht

Bin dankbar für jede Hilfe.
Außerdem wollte ich darauf hinweisen, das das kopieren und die Längenbestimmung nach Vorgabe selber programmiert werden müssen, daher nicht die strlen oder strcpy Funktionen der cstring libary.
Danke,
Blackskyliner.
-
Das Programm ist ziemlich lang und komplex. Um darin Fehler zu finden, eignet sich ideal der Einsatz eines Debuggers. Damit wirst du sicherlich schnell fündig, sobald du gelernt hast damit umzugehen. Und wenn du es erst einmal kannst, ist das ein unschätzbares Werkzeug für die Zukunft.
-
Ich bin es ja schon mit dem Code::Blocks Debugger durch gegangen und hab auch da entsprechend nicht wirklich komfortable Möglichkeiten das irgendwie "toll" zu überwachen... (Anders ausgedrückt ich hab den Fehler dadurch nicht wirklich gefunden) Das einzige was "besser" wäre, würde der Einsatz des Visual Studios Debuggers sein... Nur das ich da erstmal ewig runter laden muss bei meiner Anbindung....
Ich dachte es wäre irgend ein Simpler schon 100 fach besprochener Fehler, sodass ich vielleicht von den Erfahrungen der Mitglieder dieses Forums profitieren kann.
Aber da werde ich mich halt nochmal mit dem gdb dahinter klemmen und schauen ob mir vielleicht doch noch irgendwas auffällt.
Trotzdem danke.

Blackskyliner
-
Blackskyliner schrieb:
Ich dachte es wäre irgend ein Simpler schon 100 fach besprochener Fehler, sodass ich vielleicht von den Erfahrungen der Mitglieder dieses Forums profitieren kann.
Der Fehler riecht nach einem falsch zugewiesenen Pointer. Aber an welcher Stelle jetzt genau, dafür müsste ich erstmal das ganze Programm nachvollziehen.
-
operator+ ist falsch definiert. Passiert das bei der Konkatenation?
-
witte schrieb:
operator+ ist falsch definiert. Passiert das bei der Konkatenation?
Nee erst nach dem erneutem zuweisen, das appenden eines Strings funktioniert einwandfrei...
@SeppJ: Ja so was in der Art denk ich auch... Aber eigentlich müsste alles hin haun... Nach dem Konstruktor sind die Daten ja alle sogar DA die existieren nach dem Prozess aber kaum will ich von außen drauf zugreifen sind die Daten auf einmal leer...
Blackskyliner
-
Kein Zuweisungsoperator definiert ...
-
knivil schrieb:
Kein Zuweisungsoperator definiert ...
Ganz zu schweigen von fehlender Exceptionsicherheit.
-
Inwiefern Exceptionsicherheit?
Was kann oder muss ich denn machen damit es "sicher" ist?
-
genau so hab ich auch angefangen 
Zur Erklärung:
Bei s = "123"; würde der Compiler nach einem operator=(const char
suchen, den du aber nicht implementiert hast. Weil du aber auch keinen operator=(const String &) bereitgestellt hast, hat der Compiler diesen automatisch erstellt. Nun weis der Compiler, dass er aus "123" ein neues String-Objekt erzeugen kann und tut das auch. Dieses temporäre String-Objekt wird jetzt über den default-operator=(const String &) an s zugewiesen, aber dieser böse operator kopiert nur alle Daten-Member vom temporären String-Objekt über s drüber. Und das ist das Problem: Jetzt teilen sich beide Objekte den gleichen Pointer auf das char-array. Das temporäre String-Objekt wird nach diesem Einsatz zerstört (-> Destruktor) und löscht das char-array. Somit hat s einen pointer auf nicht mehr vorhandene Daten. Sobald du also irgend wie mit s arbeitest kracht es.String& operator=(const String & r) { if(this != &r) { int sz = r.getSize(); char * tmp = new char[sz + 1]; memcpy(tmp, r.data, sz); tmp[sz] = 0; delete [] this->data; this->data = tmp; } return *this; }Etwas Ähnliches kannst du auch bei einem String& operator=(const char * str) schreiben, dann braucht der Compiler kein temporäres Objekt erzeugen, dass er dann sowieso nach dem Kopieren gleich wieder zerstören muss.
Da gibt es ja diese Regel, dass wenn ein Objekt entweder einen Copy-Constructor oder einen Assignment-operator oder einen Destructor braucht, automatisch auch die beiden anderen notwendig sind (meistens zumindest).
Die append Methode ist zB. nicht Exception-sicher, weil zuerst delete [] data aufgerufen wird, und danach data = new char[fullLength + 1]. Wenn jetzt nicht mehr genug Speicher frei ist, löst new die Exception aus, während data auf ungültige Daten verweist. Die Exception bewirkt dann, dass dein String Objekt zerstört wird und im Destruktor rufst du damit erneut delete[] auf bereits gelöschte Daten auf -> crash.
Schreib doch anstatt data = new char[fullLength + 1] einfach data = tmp; fertig. Es ist unnötig die Daten zweimal herumzukopieren, einmal reicht.Und noch ein paar Tipps: Ersetze void append(String string) durch void append(const String & string), es erspart dir ein unnötiges temporäres Objekt.
Statt tmp[getSize()+i] = string.data[i]; speichere dir die Länge einmal in eine Variable.int iSize = getSize(); ... tmp[iSize + i] = string.data[i];Stell dir vor, du erweiterst nen 1 MB großes XML String mit nem weiteren 1 MB großen String. Da würdest du bei jedem einzelnen Zeichen per getSize() von 0 bis 1MB durchlaufen um zum '\0' zu gelangen. Das wären dann 1.000.000.000.000 Lese-Zugriffe (bzw. 1.099.511.627.776
).
Ein std::string speichert deshalb die Länge in einem weiteren Member, damit die nicht zur Laufzeit festgestellt werden muss.Ich hab ja auch mal ne String-Klasse selbst geschrieben... da lernt man einiges daraus... dennoch wird man am Ende feststellen, dass man es kaum (oder nur unwesentlich) besser als std::string machen kann

lg XOR