Tips für weitere Funktionen in eigener String-Klasse
-
Naja was du alledings besser machen könntest wäre ne Format funktion, welche mir definitv in der STL string fehlt;) also ne C++ sprintf variante, so wie sie in MFC CString Format realisiert ist...
-
EDIT: Damit sowas geht:
//format via Konstruktor double dDat=12.455; Stringclass strdouble("%f",dDat); //Format via Funktion Stringclass strdouble; strdouble.Format("f",dDat);
-
CFreak schrieb:
Naja was du alledings besser machen könntest wäre ne Format funktion, welche mir definitv in der STL string fehlt;) also ne C++ sprintf variante, so wie sie in MFC CString Format realisiert ist...
Aber bitte nicht wieder mit Ellipsen und Formatkennzeichnern, die den Datentyp beinhalten müssen. Da sollten wir in C++ drüber weg sein.
-
uhsuhz schrieb:
So ich habe jetzt mal bisschen weiter programmiert, und weitere fragen sind aufgetaucht. Ich dokumentiere mal den code.
Ganz ordentlich. Statt UINT solltest du "unsigned" nehmen, das ist gängiger. aber im prinzip ist der code ok.
Also zu dem Code besteht jetzt nur eine frage, kann man "data = new char[size]" in die Initialisierungsliste des konstruktors packen?
probier es doch aus

und ja, es geht.aber pass auf, data und size werden unabhängig von ihrer position in der initialisierungsliste sonder nur abhängig von der reihenfolge der deklaration in der klasse.
Also hab mal zuerst ne swap methode geschrieben.Funktionnieren tut es, aber schön sieht das nicht aus, meiner meinung nach, da ich den stan**** copykonstruktor selber programmieren musste, damit temp auf sc verweist.So brauche ich dann nicht den inhalt, sondern nur den zeiger auf data zu kopieren, ich muss nur den zeiger von temp auf 0 zum Schluss setzen, damit ich den speicher von this nicht lösche.Hab das mal aufgemalt:
http://img179.imageshack.us/img179/9462/swapda4.jpgDen CopyCtor musst du so oder so selber programmieren.
void Stringclass::myswap(Stringclass& sc) { Stringclass temp; temp.size = sc.size; temp.data = sc.data; sc.size = this->size; sc.data = this->data; this->size = temp.size; this->data = temp.data; temp.data = 0; }std::swap hilft dir hier:
void Stringclass::myswap(Stringclass& sc) { std::swap(size, sc.size); std::swap(data, sc.data); }du brauchst kein temporaeres Stringclass objekt dafür.
und dann noch den sc-realloc:
void Stringclass::sc_realloc(UINT newsize) { Stringclass temp(newsize,this->data); myswap(temp); }Das ist ok so. den vorteil siehst du hoffentlich:
es ist viel einfach ein realloc so zu machen als händisch alles rumzukopieren.
-
Erstmal danke an Shade Of Mine, deine Ratschläge haben mir sehr weiter geholfen, hab sehr viel in Bezug zu den klassen gelernt

Shade Of Mine schrieb:
Ganz ordentlich. Statt UINT solltest du "unsigned" nehmen, das ist gängiger. aber im prinzip ist der code ok.
hmm ich hatte UINT in der header mit typedef deklariert, darum müsste es aber klar sein, oder?
Shade of Mine schrieb:
std::swap hilft dir hier:
void Stringclass::myswap(Stringclass& sc) { std::swap(size, sc.size); std::swap(data, sc.data); }du brauchst kein temporaeres Stringclass objekt dafür.
Ich würde aber lieber alle Funktionen selber programmieren zur Übung, darum habe ich jetzt mal die Funktion neu geschrieben, sodass kein Temporäres object mehr gebildet wird, sondern nur 2 temporäre Variablen.
void Stringclass::myswap(Stringclass& sc) { UINT i_temp = sc.size; //Temporäre UINT-Variable sc.size = this->size; this->size = i_temp; char* c_temp = sc.data; //Temporärer Char-Zeiger sc.data = this->data; this->data = c_temp; }Ich würde mal sagen, dass dies schneller ist, als ein Stringclass-Object zu stellen oder?
und dann noch den sc-realloc:
void Stringclass::sc_realloc(UINT newsize) { Stringclass temp(newsize,this->data); myswap(temp); }Das ist ok so. den vorteil siehst du hoffentlich:
es ist viel einfach ein realloc so zu machen als händisch alles rumzukopieren.
Jop natürlich sehe ich dass es einfacher ist, ich hab den code ja auch so selber erdacht und geschrieben und nicht nur abkopiert von einem andern
. Ich habe diesen Konstruktor ja auch eigentlich nur wegen dieser Funktion hinzugefügt am anfang, aber jetzt sehe ich dass er auch sehr gut geeignet ist um Const-Stringclassen mit Werten zu füllen.
-
Mich wundert, dass noch niemand diesen schönen Artikel von Herb Sutter über das Design der std::string-Klasse verlinkt hat:
Monoliths "Unstrung"
-
ich hab noch ne frage, bei der überladung des >>operators:
std::istream& operator>>(std::istream& is, Stringclass& sc) { char x[100]; std::cin.getline(x,100); Stringclass temp(c_len(x),x); sc.myswap(temp); return is; }kann ich da nicht irgentwie x, als undefiniert groß nehmen, also dass man auch 1000 zeichen tippen kann oder muss man eine bestimmte größe nehmen, das heißt in diesem Fall dass nur 100 Zeichen genommen werden.
-
Shade Of Mine schrieb:
protected ist wie public: nur verwenden wenn es wirklich sein muss. strings sind aber value typen. und von value typen erbt man nicht. ergo nix protected machen.Nicht dass ich deinem Rat widersprechen wollte, aber "strings sind aber value typen" ist nicht so zwangsläufig wie du das dahinstellen willst.
Shade Of Mine schrieb:
dir fehlt ein copyctor, beachte the law of the big three
Shade Of Mine schrieb:
deine operator+ funktionen sind falsch
Shade Of Mine schrieb:
generell sollten alle funktionen die nicht zwingend member sein muessen, ausserhalb der klasse definiert werden. darunter zB die vergleichsoperatoren, find, compareto,...
Shade Of Mine schrieb:
du solltest eine schnittstelle anbieten damit man die funktionalitaet der klasse erweitern kann: iteratoren und operator[]
Shade Of Mine schrieb:
du koenntest die performance verbessern indem du mehr speicher allokierst als du jetzt brauchst um so schneller anhaengen zu koennen. oder aber du spendierst hier eine 2. klasse, einen stringbuilder der schnelles anhaengen ermoeglicht und nachher einen string liefert (wie eben zB Java StringBuilder)
Shade Of Mine schrieb:
du solltest auf constant correctness achten
Shade Of Mine schrieb:
eine implementierung fuer swap fehlt, um 2 string objekte effizient miteinander austauschen zu koennen ohne alles kopieren zu muessen
Shade Of Mine schrieb:
index, length, etc sollten unsigned int sein, damit verhinderst du automatisch dass jemand negative werte uebergibtJa, ich plädiere ebenfalls für
std::size_toder sowas
-
Erhard Henkes schrieb:
- alle Zeichen umdrehen (zum rückwärts Lesen üben)
- entschlüsseln mit einem weiteren Schlüsselstring
- Verschlüsseln nach verschiedenen Techniken
- Umwandlung in Zahlenformate
- automatische Rechtschreibprüfung
- Übersetzung in andere Sprachen
- TxtToSpeech
- usw.Auch wenn dir das gegebenenfalls klar ist, davon hat rein gar nichts irgendetwas in einer Stringklasse verloren.
-
uhsuhz schrieb:
hmm ich hatte UINT in der header mit typedef deklariert, darum müsste es aber klar sein, oder?
Technisch ist es kein Problem, es ist halt lediglich eine Konvention die man sich angewoehnt hat. UINT kann jeder als unsigned int lesen, das ist kein Problem, aber es sieht vertrauter aus wenn dort unsigned stehen wuerde - mehr nicht.
Wobei wie einer meiner vorposter richtig gesagt hat, eigentlich std::size_t der richtige Typ waere. Aber das sind details...
Ich würde aber lieber alle Funktionen selber programmieren zur Übung, darum habe ich jetzt mal die Funktion neu geschrieben, sodass kein Temporäres object mehr gebildet wird, sondern nur 2 temporäre Variablen.
Auch gut. Ich wuerde dann zwar ein eigenes myswap() schreiben, aber dazu braeuchte man dann templates und das ist uU zu verwirrend. Dennoch hier kurz wie swap implementiert ist:
template<typename T> void swap(T& a, T& b) { T c(a); a=b; b=c; }aber dein Stringclass::myswap ist schon in ordnung so.
Ich würde mal sagen, dass dies schneller ist, als ein Stringclass-Object zu stellen oder?
Exakt.
Die ganze Idee hinter einem swap ist, dass diese Operation quasi keine Zeit kostet. Man will hier keinen Speicher allokieren und nicht viel kopieren. Wir koennten das swap ja theoretisch auch so implementieren dass wir den ganzen Speicher immer herum kopieren:
void Stringclass::myswap(Stringclass& other) { Stringclass temp(*this); *this = other; other = temp; }Aber das waere sehr sehr langsam und wuerde deshalb keinen Sinn machen. Da swap so schnell ist, kann man ja auch das realloc wie du es schoen gemacht hast ueber swap implementieren.
Wenn du dich spaeter mal mit exceptions befasst, wirst du sehen dass swap auch noch andere vorteile hat, weil es die nothrow garantie unterstuetzt - aber das braucht dich erst spaeter zu interessieren.
ich hab noch ne frage, bei der überladung des >>operators:
std::istream& operator>>(std::istream& is, Stringclass& sc) { char x[100]; std::cin.getline(x,100); Stringclass temp(c_len(x),x); sc.myswap(temp); return is; }kann ich da nicht irgentwie x, als undefiniert groß nehmen, also dass man auch 1000 zeichen tippen kann oder muss man eine bestimmte größe nehmen, das heißt in diesem Fall dass nur 100 Zeichen genommen werden.
Ja, kannst du. Der Trick besteht darin dir zu ueberlegen wie lange du einlesen willst. std::string liest zB bis zum ersten whitespace (also leerzeichen, tab, newline,...) ein. Wenn wir jetzt annehmen du willst das selbe machen:
Das ganze ist jetzt eine einfache Variante, da eine wirklich korrekte Verison ziemlich haesslich und umstaendlich ist (weil man zB checken muss ob beim lesen aus dem stream ein fehler aufgetreten ist, eingabe formatierungen, locales, etc):
bool is_break_char(int c) { return c==' ' || c=='\t' || c=='\n' || c=='\r'; } istream& operator>>(istream& is, Stringclass& s) { int c; s.clear(); while( (c=is.get()) != EOF ) { if(is_break_char(c)) { return is; } s+=c; } return is; }am anfang loeschen wir den string (wir koennten auch auf einem temporaeren objekt arbeiten und dann am ende swap machen) und lesen solange aus dem stream ein bis EOF kommt. EOF - End Of File, zeigt an, dass kein Input mehr kommen kann.
Wenn der gelesene char ein endzeichen ist, dass du wie gesagt selber definieren musst, beenden wir das einlesen. Andernfalls haengen wir das zeichen an unseren string an.
Das ganze ist jetzt natuerlich sehr sehr langsam, weil wir immer nur 1 zeichen anhaengen und wir dadurch dauernd den string vergroessern muessen - da muesste man jetzt mit einem buffer arbeiten der zB immer alle 100 zeichen oder so den inhalt an den string anhaengt.
-
danke für die hilfe^^
ich habe aber den realloc rausgenommen, da er überflüssig geworden ist.
Mein Denkweg:
Ich hatte ja am Anfang:
void Stringclass::sc_realloc(UINT newsize) { Stringclass temp(newsize,this->data); myswap(temp); } Stringclass::Stringclass(UINT scsize, const char* c) :size(scsize),data(new char[size]) { mycopy(c,data,size); }und dann noch eine funktion die dies benötigt.
Da musste ich zuerst eine Stringclass erstellen, die wurde dann mit iorgentwelchen nicht gewollten Werten erstellt, dann übergab ich sie an sc_realloc, und schon wieder wurde eine Stringclass mit irgentwelchn Werten gefüllt, und dann erst wurde alles rüberkopiert.
Das habe ich dann aber idiotisch gefunden, und hab erstmal sofort eine Stringclass aus der operator+ funktion erstellt mit der richtigen größe und dann kopiert. Dann würde aber trotzdem die Stringclass mit leeren Werten gefüllt werden. Also habe ich sofort das erste Array mitgegeben, und nur das 2 zweite array später reinkopiert.
Stringclass Stringclass::operator+(const Stringclass& sc) const { Stringclass scresult(this->size+sc.size,this->data); mycopy(sc.data,scresult.data,sc.size,this->size); return scresult; }Somit ist der code ja schon viel kleiner geworden. Dann habe ich mir gedacht, da der Konstruktor:
Stringclass(UINT scsize, const char* scdata);ja eigentlich nur von den Funktionen der Stringclass aufgerufen wird und am besten nicht vom Anwender, da es eine potentielle Fehlerquelle ist, aufgrund der freiwählbaren Größe, diesen Konstruktor privat zu machen. Außerdem habe ich mir einen Konstruktor hinzugefuegt, der nur ein Char-Array nimmt und dann selber die Größe erkennt, somit kann man jetzt keine Größe mehr selber eingeben.
Somit brauche ich das sc_realloc auch nicht mehr.Dann dachte ich dass es aber efiizienter wäre, wenn ich im privaten Konstruktor sofort die 2 Arrays reinkopiere, so habe ich einen defaultparameter mit dem 2. Array reingebracht und das habe ich jetzt(mit der operator+ funktion)
Stringclass::Stringclass(UINT length1, const char* scdata1,UINT length2, const char* scdata2) :size(length1+length2),data(new char[size]) { mycopy(scdata1,data,length1); mycopy(scdata2,data,length2,length1); } Stringclass Stringclass::operator+(const Stringclass& sc) const { Stringclass scresult(this->size,this->data,sc.size,sc.data); return scresult; }Ich hätte auch den Konstruktor Stringclass(const char* c) aufrufen können, aber da der zuerst mal die Länge des Array mittels c_len(c) herausfinden musste, ist es glaube ich effizienter ihm die länge die man bereits hat mitzuteilen.
Erreicht habe ich damit:
-Benutzer kann nicht mehr eine Stringclass mit beliebiger größe eingeben
-Code ist kürzer geworden(von 5 auf 2 Zeilen in den Operator+)
-Speicher wird nicht mehr mit irgentwelchen Schrott gefüllt
-weniger Methoden aufrüfe, vorher 4, jetzt 2 und statt 2 Konstruktore wird jetzt nur noch einer aufgerufen.So jetzt wollte ich euch fragen, waren meine Überlegungen ok, oder einfach nur schrott? Ist es jetzt schneller als vorherm oder...?
Danke schonmals im Voraus
uhsuhz
-
Hast du denn jetzt die Version bzgl. operator >> von Shade of Mine übernommen?
Denn in deiner Version ist noch ein Fehler:is.getline(x,100);Du kriegst den Stream ja als Parameter übergeben und dies muß nicht unbedingt std::cin sein, sondern kann ja auch ein ifstream oder ähnliches sein.
-
ja hab ich, ist besser, nur dass ich nicht bei Leerzeichen aufhöre, sondern nur bei \n, \t und \r
vielleicht setzte ich später alles online, damit ihr schuaen könnt^^
noch eine Frage, reichen diese +operatoren?
Stringclass + Stringclass
Stringclass + Char*
Stringclass + Char
Char* + Stringclass
Char + Stringclassdies sind die operatoren mit rückgabewert = Stringclass die ich implementieren wollte, oder fehlen noch welche?
noch ne frage:
bei replace, wenn ich "Test" hab und ich versuche mit replace(1,"Super") zu ersetzen, dann würde Super ja Test überschreiten, also TSuper, aber dann hab ich die buchstaben 'e' und 'r' out of range, wird da normalerweise gar nichts kopiert, oder abgeschnitten, oder string vergrößert? ich habe jetzt mal gemacht, dass return = false und nichts kopiert wird
-
uhsuhz schrieb:
So jetzt wollte ich euch fragen, waren meine Überlegungen ok, oder einfach nur schrott? Ist es jetzt schneller als vorherm oder...?
Ja, es ist OK so.
Die wirklich Frage dabei ist nur, wie schnell du anhaengen willst an den String.Deine Variante erlaubt kein schnelles append, da du exakt soviel speicher hast wie du brauchst. std::string arbeitet zB mit mehr speicher als noetig, dafuer ist ein append ohne reallokation moeglich.
wenn du bei deiner variante bleibst, was durchaus ok ist - solltest du es wie Java machen und einen StringBuilder anbieten der wenn ich 10 Teilstrings habe diese schnell zu einem Stringclass Objekt zusammenfuegen kann (ohne dass man 10 mal nue allokieren muss).
operator+ ist so ok.
bei einem replace wird normalerweise der string dann angepasst. also vergroessert bzw. verkleinert wenn die beiden suchstrings nicht gleiche laenge haben.
-
uhsuhz schrieb:
noch eine Frage, reichen diese +operatoren?
Stringclass + Stringclass
Stringclass + Char*
Stringclass + Char
Char* + Stringclass
Char + Stringclassdies sind die operatoren mit rückgabewert = Stringclass die ich implementieren wollte, oder fehlen noch welche?
Mach statt "char*" als Parameter lieber "const char*", du willst die Zeichenkette schließlich nicht verändern und wenn ich nur einen Zeiger auf eine konstante Zeichenkette hab, soll das auch funktionieren

Übrigens: Wenn du den Operator "StringClass + Char*" (oder auch den anderen) global definierst, also nicht in der Klasse und mit
Stringclass operator+( const Stringclass& a, const char* b ), dann funktioniert automatisch auch "const char* + Stringclass"

-
Badestrand schrieb:
Übrigens: Wenn du den Operator "StringClass + Char*" (oder auch den anderen) global definierst, also nicht in der Klasse und mit
Stringclass operator+( const Stringclass& a, const char* b ), dann funktioniert automatisch auch "const char* + Stringclass"

performancemaessig aber nicht ideal...
-
Inwiefern?
-
Badestrand schrieb:
Inwiefern?
weil dann der operator +(string,string) aufgerufen wird

-
oh hatte ich vergessen zu posten, ist natürlich alles const
-
so habs jetzt geschafft die replace-methode sauber hinzuschreiben indem ich die cut und schließlich die insert methode nehme, so wird automatisch speicher vergrößert oder verkleinert
bool Stringclass::replace(unsigned int index, const Stringclass& sc, unsigned int length) { if(this->cut(index,length) && this->insert(index,sc,length)) return true; return false; }@Shade of Mine
Wie meinst du das mit dem Stringbuilder? wie kann er schneller anhängen, ich habeleider da gar keine ahnung wie ich das machen soll, vielleicht einen kleinen Denkanstoß wie das funktionnieren könnte oder eine kleine Hilfe wäre nett
Danke , uhsuhz