X
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