[C++] Eigene toUpperCase Methode funktioniert nicht

Kirodema

Grünschnabel
Hi,
ich lern seit etwa einer Woche C++ und wollte heute anfangen, mir eine eigene String-Klasse zu kreieren (ähnlich wie die von Java). Leider scheiterts bei mir schon bei einer simplen toUpperCase-Funktion. Das Programm bricht mit "MyString.exe hat ein Problem festgestellt und muss beendet werden." ab.
Hier mal der Code:

Header:
C++:
/*
 * String.h
 *
 *  Created on: 13.04.2009
 *      Author: Kirodema
 */
#include <ostream>
#ifndef STRING_H_
#define STRING_H_
using namespace std;

class String {
	char *str;
	unsigned int length;
public:
	String();
	String(char str[]);
	~String();
	friend ostream &operator << (ostream &stream, String str);
	unsigned int getLength() {return length;}
	String toUpperCase();

};

#endif /* STRING_H_ */

CPP-File
C++:
/*
 * String.cpp
 *
 *  Created on: 13.04.2009
 *      Author: Kirodema
 */

#include "String.h"

String::String() {
	// TODO Auto-generated constructor stub

}

String::String(char *str) {
	this->str = str;
	length = 0;
	while(*str) {
		length++;
		str++;
	}
}

String::~String() {
	// TODO Auto-generated destructor stub
}

String String::toUpperCase() {
	char *tmp = str;
	while (*tmp) {
		*tmp = *tmp & 223; // Bei der Zeile scheint der Hacken zu liegen
		tmp++;
	}
	return String(tmp);
}

ostream &operator << (ostream &stream, String str) {
	stream << str.str;
	return stream;
}
 
Hi, ich hatte vor Jahren auch mal so eine Funktion gebastelt, jedoch anderer Ansatz.

Zu jedem Zeichen, was vorher überprüft wird (sihe ASCII tabelle -> Google) wird einfach ein wert (ich meine es war 32 oder so..) addiert, einfach Grisbuchtaben minus kleinbuchstabe = Wert. Mit ein paar ausnahmen für zahlen und zeichen die man durch if's löst hats bei mir wunderprächtig funktiniert.

Doch was anderes, warum schreibst du die Stringlasse neu um C++ zu erlernen?! Mach lieber tutorials und löse Aufgaben, das bingt mehr, wie wen du das Rad neuerfindest;)
 
Ich mach das nur als kleine Übung, da mir nichts besseres einfällt :).
Und das Problem liegt nicht an der Rechnung sondern an der Zuweisung.
Also
C++:
 *tmp = *tmp & 223
mag nicht. Es funktioniert auch nicht mit der toupper() Funktion.
 
Es gibt nur die Windows eigene "Das Programm hat einen Fehler festgestellt und muss beendet werden blabla."-Fehlermeldung und fragt, ob er nen Problembericht an Microsoft schicken soll oder nicht. Wenn gewünscht, kann ich ja die technischen Informationen aus dem Bericht hier reinkopieren (sehr lang, deshalb frag ich erst).

Edit: und wie schon gesagt: mit toupper() aus <cctype> funktionierts auch nicht. Jegliche Zuweisung bringt das Programm zum Absturz
 
Also lässt es sich kompilieren, dan wäre noch Kompiler hilfreich zu wissen und velche version du benuzt. Sonst ist es schwierig.
Und der Debug sagt auch nix dazu?
 
Hi.

@Kirodema: Deine Klasse hat mehrere Probleme.

Zuerst einmal muß der Speicher für den String den die Klasse verwalten soll bereits alloziert sein. Wie machst du das denn? Kann es sein, das du es so machst:
C++:
char* x = "abcdefg";

String str(x);
Das ist dann allerdings falsch, da der Speicher auf den x zeigt nicht veränderbar ist.

Zeig einfach mal ein komplettes (Minimal-) Beispielprogramm wo der Fehler auftritt.

Gruß

PS: Haken schreibt man nicht mit ck.
 
Oha, da gibt es gleich zu mehreren Stellen was zu sagen…

Header:
C++:
/*
 * String.h
 *
 *  Created on: 13.04.2009
 *      Author: Kirodema
 */
#include <ostream>
#ifndef STRING_H_
#define STRING_H_
using namespace std;
Eine solche Zeile niemals in eine Headerdatei schreiben. Damit verschmutzt du den Namensraum jeder Datei, in der du diese Headerdatei einbindest.

C++:
	friend ostream &operator << (ostream &stream, String str);
Das String-Objekt sollte man hier besser per Referenz übergeben, da sonst bei jedem Aufruf dieser Operator-Überladung eine temporäre Kopie dieses Objektes angelegt wird.

C++:
	unsigned int getLength() {return length;}
Diese Methode könnte (und sollte) man als const deklarieren.

C++:
String::String() {
	// TODO Auto-generated constructor stub

}
Das TODO bitte nicht übersehen. Im Konstruktor immer sämtliche Instanzvariablen initialisieren.

C++:
String::String(char *str) {
	this->str = str;
	length = 0;
	while(*str) {
		length++;
		str++;
	}
}
Ich halte es für keine sehr gute Idee, den Zeiger „von außen“ zu verwenden. Das führt zu allerhand Problemen in der Speicherverwaltung: Wer löscht den reservierten Speicher für die Zeichenkette wieder? Woher weiß der löschende Programmteil, dass der Speicher nicht noch in irgendeinem String verwendet wird? Was passiert, wenn man eine Stackadresse übergibt? Daher: unbedingt im Konstruktor einen eigenen Speicherbereich reservieren und eine Kopie der Zeichenkette anlegen.

C++:
String::~String() {
	// TODO Auto-generated destructor stub
}
Hier würde man dann sinnvollerweise den reservierten Speicherbereich wieder freigeben.

C++:
String String::toUpperCase() {
	char *tmp = str;
	while (*tmp) {
		*tmp = *tmp & 223; // Bei der Zeile scheint der Hacken zu liegen
		tmp++;
	}
	return String(tmp);
}
Der Fehler hier hängt direkt mit meinem Kommentar zu deinem Konstruktor zusammen. Der Zeiger könnte hier z.B. auf einen Stackbereich verweisen, welcher nicht mehr gültig ist, weswegen der Versuch darauf zu schreiben mit einer Zugriffsverletzung scheitert. Abgesehen davon veränderst du in dieser Methode den String und gibst dann ein neues String-Objekt mit identischem Inhalt zurück – Sinn?

Was deiner Klasse dann auch noch fehlt, sind ein sinnvoller Kopierkonstruktor und Zuweisungsoperator.

Grüße, Matthias
 
Hi.

@Kirodema: Deine Klasse hat mehrere Probleme.

Zuerst einmal muß der Speicher für den String den die Klasse verwalten soll bereits alloziert sein. Wie machst du das denn? Kann es sein, das du es so machst:
C++:
char* x = "abcdefg";

String str(x);
Das ist dann allerdings falsch, da der Speicher auf den x zeigt nicht veränderbar ist.

Zeig einfach mal ein komplettes (Minimal-) Beispielprogramm wo der Fehler auftritt.

Gruß

PS: Haken schreibt man nicht mit ck.
Hier ein kurzer Beispielcode, wo der Fehler auftritt:
C++:
int main(int argc, char **argv) {
	String str = "hallo welt";
	cout << str.toUpperCase();
	return 0;
}

Ich halte es für keine sehr gute Idee, den Zeiger „von außen“ zu verwenden. Das führt zu allerhand Problemen in der Speicherverwaltung: Wer löscht den reservierten Speicher für die Zeichenkette wieder? Woher weiß der löschende Programmteil, dass der Speicher nicht noch in irgendeinem String verwendet wird? Was passiert, wenn man eine Stackadresse übergibt? Daher: unbedingt im Konstruktor einen eigenen Speicherbereich reservieren und eine Kopie der Zeichenkette anlegen.
Kannst du mir bitte sagen, wie man einen Speicherbereich reservieren kann? Ich bin, was es C++ angeht, noch ein ziemlicher Anfänger.

Was deiner Klasse dann auch noch fehlt, sind ein sinnvoller Kopierkonstruktor und Zuweisungsoperator.
Was heisst, ein sinnvoller Kopierkonstruktor und Zuweisungsoperator? Reicht etwa eine Eins-zu-Eins Kopie nicht bei dem String? Oder hängt das dann mit der Speicherreservierung zusammen? (Sorry, hab von der Speicherreservierung noch null Plan)

MfG
Kirodema

Edit: das neue String-Objekt hat den Sinn, dass das Original unverändert bleibt. Wenn man will, kann man dann aber auch den String als UpperCase speichern.
 
Zuletzt bearbeitet:
Kannst du mir bitte sagen, wie man einen Speicherbereich reservieren kann? Ich bin, was es C++ angeht, noch ein ziemlicher Anfänger.
Dafür gibt es in C++ die Operatoren new/new[] und delete/delete[]. Ich weiß zwar nicht nach welchem Buch oder welcher Anleitung du die Sprache lernst, aber das sollte auf jeden Fall irgendwo erwähnt sein.

Was heisst, ein sinnvoller Kopierkonstruktor und Zuweisungsoperator? Reicht etwa eine Eins-zu-Eins Kopie nicht bei dem String? Oder hängt das dann mit der Speicherreservierung zusammen? (Sorry, hab von der Speicherreservierung noch null Plan)
Wenn für dich Zeiger und Speicherverwaltung noch böhmische Dörfer sind, dann halte ich es nicht für recht sinnvoll, wenn ich jetzt versuche dir darauf aufbauende Konzepte zu vermitteln. Daher nur in Kürze: der Kopierkonstruktor sollte nicht den Zeiger des zu kopierenden Objektes übernehmen (so wie es der Standardkopierkonstruktor machen würde), sondern selber Speicher reservieren und eine echte Kopie der Zeichenkette anlegen. Der Zuweisungsoperator dann ebenso.

Edit: das neue String-Objekt hat den Sinn, dass das Original unverändert bleibt. Wenn man will, kann man dann aber auch den String als UpperCase speichern.
Du veränderst das Original allerdings. Wenn du nicht verstehst, warum, dann hast du dir für den Anfang mit der String-Klasse vielleicht etwas zu viel vorgenommen. Andererseits wächst man natürlich auch an Herausforderungen :)

Grüße, Matthias
 
Zurück