[C++] Eigene toUpperCase Methode funktioniert nicht

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.
Ich lerns vom E-book "C++ Beginners Guide" von Microsoft. Bin grad bei Kapitel 11 und hab grad nachgeschaut: new und delete kommt in Kapitel 12

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.
Zeiger sind für mich noch von einem leichten Nebel umschlossen, Speicherverwaltung muss ich noch lesen hab ich gerade mitbekommen.

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 :)
Um ehrlich zu sein, versteh ich das grad nicht so ganz? Zeigt etwa mit
C++:
char *tmp = str;
*tmp auf die selbe Stelle im Speicher wie *str?
 
Um ehrlich zu sein, versteh ich das grad nicht so ganz? Zeigt etwa mit
C++:
char *tmp = str;
*tmp auf die selbe Stelle im Speicher wie *str?
Die Zeigervariable tmp speichert nach der Zuweisung denselben Wert (dieselbe Speicheradresse) wie str. Die Anweisung bewirkt nichts anderes. Insofern zeigen beide Zeiger dann tatsächlich auf denselben Speicherbereich.

Grüße, Matthias
 
Hier ein kurzer Beispielcode, wo der Fehler auftritt:
C++:
int main(int argc, char **argv) {
	String str = "hallo welt";
	cout << str.toUpperCase();
	return 0;
}
Das ist nur eine etwas kürzere Variante für den von mir geposteten Code. Stringliterale sind in C/C++ nicht änderbar. Das Problem könntest du vermeiden, indem du ein char Array verwendest:
C++:
char s[] = "hallo welt";
String str(s);
Das ist natürlich grundsätzlich keine gute Lösung. Den Speicher solltest du innerhalb der Klasse selbst verwalten, wie von Matthias schon angesprochen.
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.
Dann mußt du aber irgendwo eine Kopie der ursprünglichen Daten anlegen und ggf. auch wieder freigeben.
Um ehrlich zu sein, versteh ich das grad nicht so ganz? Zeigt etwa mit
C++:
char *tmp = str;
*tmp auf die selbe Stelle im Speicher wie *str?
Ja, natürlich zeigt tmp zeigt auf die gleiche Stelle wie str, d.h. *tmp == *str.

Gruß
 
Ich hab jetzt die Sachen mit new und delete mal nachgelesen und hab mal versucht es in die Stringklasse einzuarbeiten. Hab auch noch eine Copyconstructor eingebaut. Leider besteht das selbe Problem weiterhin.
Hier mal der überarbeitete Code:

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


class String {
	char **str;
	unsigned int length;
public:
	String();
	String(char *str);
	~String();
	String(const String &s);

	const unsigned int getLength() {return length;}
	String toUpperCase();

	friend std::ostream &operator << (std::ostream &stream, String &str);
	String operator=(char *str);
};

#endif /* STRING_H_ */

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

#include "String.h"
#include <new>
#include <iostream>
using namespace std;

String::String() {
	str = new char*;
}

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

String::String(const String &s) {
	str = new char* (*s.str);
	length = s.length;
}

String::~String() {
	delete str;
}

String String::toUpperCase() {
	String ret = *this;
	for (unsigned int i = 0; *ret.str[i]; i++)
		*ret.str[i] = *ret.str[i] & 223;    //Wieder bei der Zuweisung das Problem
	return ret;
}

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

ostream &operator << (ostream &stream, String &str) {
	stream << *str.str;
	return stream;
}

Lässt sich alles fehlerfrei kompilieren mit MinGW. Ich glaub aber auch, dass ich beim Copyconstructor was falsch gemacht hab :/.

MfG
Kiro
 
Hallo Kirodema,

der einzige Unterschied zu vorher ist jetzt, dass du den Speicher für deinen Zeiger händisch reservierst. Du solltest aber stattdessen den Speicher für die Zeichenkette reservieren und in diesen Speicherbereich dann Zeichen für Zeichen aus der ursprünglichen Zeichenkette kopieren. Vielleicht macht es ein kleines Beispiel deutlicher:
C++:
#include <iostream>
using std::cout;

int main(void) {
  char str[] = "tutorials.de";
  char *str2 = str;

  /*
   * Identische Speicheradresse
   */
  cout << "Speicheradresse von str:  " << (int)str << std::endl;
  cout << "Speicheradresse von str2: " << (int)str2 << std::endl;

  /*
   * Veränderung von str wirkt sich auf str2 aus
   */
  str[0] = 'T';
  cout << "Inhalt von str:  " << str << std::endl;
  cout << "Inhalt von str2: " << str2 << std::endl;
  
  /*
   * Kopie anlegen
   */
  // Länge bestimmen:
  size_t str_length = 0;
  while (str[str_length] != '\0') str_length++;
  // Speicher reservieren
  str2 = new char[str_length+1]; // +1 wegen Nullterminierung
  // Kopieren
  for (size_t i = 0; i < str_length; ++i)
    str2[i] = str[i];
  str2[str_length] = '\0'; // Nullterminierung

  cout << "Speicheradresse von str2: " << (int)str2 << std::endl;
  cout << "Inhalt von str2: " << str2 << std::endl;

  /*
   * Veränderung von str wirkt sich nicht mehr auf str2 aus, da anderer Speicherbereich
   */
  str[0] = 't';
  cout << "Inhalt von str:  " << str << std::endl;
  cout << "Inhalt von str2: " << str2 << std::endl;

  // Nicht vergessen: Speicher freigeben!
  delete[] str2;

  return 0;
}

Das mit dem const bei getLength war übrigens so gemeint, dass du die Methode als const deklarierst (also unsigned int getLength() const {return length;}), nicht den Rückgabewert. Damit gibst du zu Verstehen, dass die Methode das Objekt nicht verändert, also auch auf konstante Instanzen angewandt werden kann. Für toUpperCase würde das dann auch gelten.

Grüße, Matthias
 
Ah, ok, danke.
Eine Frage noch:
Wenn ich jetz einem Stringobjekt einen neuen String zuweisen will, also
C++:
String x = "Hallo Welt";
x = "neuer string";
, muss ich dann intern zuerst "delete str" machen und dann neu zuweisen, oder kann ich gleich sagen "new char[length+1]"?
 
Du musst delete[] str ausführen (die eckigen Klammern sind wichtig!), da du sonst ein Speicherleck hast.

Übrigens fehlt dir immer noch eine passende Überladung des Zuweisungsoperators für die Zuweisung von Instanzen deiner Klasse. Sonst erzeugt der Compiler selber einen, der wieder nur den Zeiger kopiert (nicht aber die Zeichenkette).
 
Danke, jetzt funktioniert die Klasse schon etwas besser :).
Leider bricht aber die toUpperCase()-Funktion nach dem ersten Leerzeichen ab und ich versteh nicht, warum:

C++:
//Copyconstructor
String::String(const String &s) {
	length = s.length;
	str = new char[length + 1];
	for (size_t i = 0; i < length; i++)
		str[i] = s.str[i];
	str[length] = '\0';
}
String String::toUpperCase() {
	String ret = *this;
	for (size_t i = 0; i < ret.length; i++)
		ret.str[i] = ret.str[i] & 223;
	return ret;
}

MfG
Kiro

PS: Danke für deine Geduld mit mir bis jetz :)

Edit: Es sollte zumindest die Leerzeichen durch '0' ersetzen. Zumindest hab ich
C++:
cout << (' ' & 223);
ausprobiert und es gab 0 aus.

Edit2: Problem gelöst: mit isalpha(str[i]) prüfen obs ein Buchstabe is, dann macht er auch den ganzen String.
 
Zuletzt bearbeitet:
Zurück