String-Klasse als Vorbild gesucht
-
Hier habe ich einen Ansatz für eine String-Klasse gepostet, der ist aber leider auch nicht besonders gut, zumindest copy-con, op= kannst du anschauen:
http://www.c-plusplus.net/forum/viewtopic.php?t=83500In der Literatur oder im Internet fällt mir jetzt auch kein vorbildliches Beispiel ein. Muss nochmal in meine Bücher schauen.
Bei Till Jeske, C++, Addison-Wesley findet sich eine rudimentäre Stringklasse, die aber nicht vollständig ist.
-
Wär es bei ner Stringklasse nicht praktischer, wenn operator[] eine Referenz auf einen char zurückgeben würde?
-
// [] char MyString::operator[]( int i ) const { if( i >= 0 && i < getLaenge() ) { return pData_[i]; } else { return '\0'; } }
Meinst du dies hier? Eine Referenz auf einen char? hmmm.
Ich denke, nein.
-
Hi!
Ich habe mal eben die Stringklasse hochgeladen:
Header: s. Erhard Henkes
Cpp: s. Erhard HenkesCode-Hacker
-
Damit man leichter diskutieren kann, hier der Code (von Code-Hacker):
#include <conio.h> #include <iostream> #include <vector> #include <cassert> using namespace std; class EString { private: static const unsigned long FWDBUFFER=15; static const unsigned long INPBUFFER=200; char* string; unsigned long len; unsigned long bufsize; void replace(const char*); void replace(const vector<char>& v); void insert(unsigned long, unsigned long, const char*); public: EString(); EString(const char*); EString(const char); EString(const EString&); ~EString(); EString& operator=(const EString&); EString& operator=(const char*); EString& operator=(const char); friend ostream &operator<<(ostream&, const EString&); friend istream &operator>>(istream&, EString&); EString& operator+=(const EString&); EString& operator+=(const char*); EString& operator+=(const char); friend const EString operator+(const EString&, const EString&); const EString operator+(const char*) const; const EString operator+(const char) const; friend const EString operator+(const char*, const EString&); friend const EString operator+(const char, const EString&); char &operator[](unsigned long); const char &operator[](unsigned long) const; EString operator()(unsigned long, unsigned long) const; bool operator<(const EString&) const; bool operator<=(const EString&) const; bool operator==(const EString&) const; bool operator!=(const EString&) const; bool operator>=(const EString&) const; bool operator>(const EString&) const; }; /* ** Private Methoden (technische Methoden) */ void EString::replace(const char *s) { if(string) delete[](string); len=strlen(s); bufsize=FWDBUFFER+len+1; string=new(char[bufsize]); assert(string!=0); strcpy(string,s); } void EString::replace(const vector<char>& v) { if(string) delete[](string); len=v.size(); bufsize=FWDBUFFER+len+1; string=new(char[bufsize]); assert(string!=0); unsigned int x=0; for (;x<v.size();x++) string[x]=v[x]; string[x]=0; } void EString::insert(unsigned long pos, unsigned long slen, const char *s) { if(!string) { len=slen; bufsize=FWDBUFFER+len+1; string=new(char[bufsize]); assert(string!=0); strcpy(string,s); return; } else { if((len+slen+1)<=bufsize) { for(unsigned long x=len+1;x>=pos+1;x--) string[x+slen-1]=string[x-1]; for(unsigned long x=0;x<slen;x++) string[x+pos]=s[x]; len+=slen; } else { bufsize=FWDBUFFER+len+slen+1; char *sptr=new(char[bufsize]); assert(sptr!=0); unsigned long y=0,x; for(x=0;x<pos;x++) sptr[y++]=string[x]; for(x=0;x<slen;x++) sptr[y++]=s[x]; for(x=pos;x<=len;x++) sptr[y++]=string[x]; len+=slen; delete[](string); string=sptr; } } } /* ** Konstruktoren und Destruktoren */ EString::EString(void) { len=0; bufsize=0; string=0; } EString::EString(const char *s) { string=0; replace(s); } EString::EString(const char c) { string=0; char s[2]; s[0]=c; s[1]=0; replace(s); } EString::EString(const EString &s) { string=0; replace(s.string); } EString::~EString() { if(string) delete[](string); } /* ** Zuweisung */ EString &EString::operator=(const EString &s) { if(this==&s) return(*this); replace(s.string); return(*this); } EString &EString::operator=(const char *s) { replace(s); return(*this); } EString &EString::operator=(const char c) { char s[2]; s[0]=c; s[1]=0; replace(s); return(*this); } /* ** Ein-/Ausgabe-Operatoren */ ostream &operator<<(ostream &ostr, const EString &s) { if(s.len) ostr << s.string; return(ostr); } istream &operator>>(istream &istr, EString &s) { char c; vector<char> inp; while(!isspace(c=static_cast<char>(istr.get()))) inp.push_back(c); s.replace(inp); return(istr); } /* ** Additions-Operatoren */ EString &EString::operator+=(const EString &s) { insert(len,strlen(s.string),s.string); return(*this); } EString &EString::operator+=(const char *s) { insert(len,strlen(s),s); return(*this); } EString &EString::operator+=(const char c) { insert(len,1,&c); return(*this); } const EString operator+(const EString &s1, const EString &s2) { EString tmp=s1.string; tmp.insert(tmp.len,strlen(s2.string),s2.string); return(tmp); } const EString EString::operator+(const char *s) const { EString tmp=string; tmp.insert(len,strlen(s),s); return(tmp); } const EString EString::operator+(const char c) const { EString tmp=string; tmp.insert(len,1,&c); return(tmp); } const EString operator+(const char *s, const EString &str) { EString tmp=s; tmp.insert(tmp.len,strlen(str.string),str.string); return(tmp); } const EString operator+(const char c, const EString &str) { EString tmp=c; tmp.insert(tmp.len,strlen(str.string),str.string); return(tmp); } /* ** Indizierungsoperator */ char &EString::operator[](unsigned long p) { assert(p<len); return(string[p]); } const char &EString::operator[](unsigned long p) const { assert(p<len); return(string[p]); } /* ** Funktionsaufrufoperator */ EString EString::operator()(unsigned long p, unsigned long l) const { assert((p<len)&&((p+l)<=len)); EString tmp=""; tmp.insert(0,l,string+p); return(tmp); } /* ** Vergleichsoperatoren */ bool EString::operator<(const EString &s) const {return(strcmp(string,s.string)<0);} bool EString::operator<=(const EString &s) const {return(strcmp(string,s.string)<=0);} bool EString::operator==(const EString &s) const {return(strcmp(string,s.string)==0);} bool EString::operator!=(const EString &s) const {return(strcmp(string,s.string)!=0);} bool EString::operator>=(const EString &s) const {return(strcmp(string,s.string)>=0);} bool EString::operator>(const EString &s) const {return(strcmp(string,s.string)>0);} int main() { EString s1 = "Hallo "; EString s2 = "schoene "; EString s3 = "Welt"; cout << s1+s2+s3 << endl; getch(); }
-
Erhard Henkes schrieb:
// [] char MyString::operator[]( int i ) const { if( i >= 0 && i < getLaenge() ) { return pData_[i]; } else { return '\0'; } }
Meinst du dies hier? Eine Referenz auf einen char? hmmm.
Ich denke, nein.Warum das? Dann kann ich ja das folgende gewohnte gar nicht machen:
MyString s = "hallo"; void translate() { s[1] = 'e'; }
-
Hi!
War mir nicht sicher ob ich den posten darf, da der sehr lang ist, aber wenn er hier ist, dann nehm ich ihn wieder runter.
Code-Hacker
-
Wozu braucht man diesen FWDBUFFER ? z.B. in der Funktion replace()
-
Hi!
Da der String wachsen kann/soll wird dem String gleich mehr Speicher zur Verfügung gestellt um nicht bei jeder Vergrößerung einen neuen Speicherbereich anfordern zu müssen. Somit enthält die Konstante FWDBUFFER die Anzahl Zeichen die bei jeder neuen Reservierung von Speicher zusätzlich belegt werden sollen.
Könnte man also auch weglassen, war ne Idee des Autors...
Code-Hacker
-
@KPC: du hast recht, EString verwendet ebenfalls char& als Rückgabetyp bei []
-
[Müll]
Ich vermisse Konstruktoren für Typen wie long, double, std::string, std::wstring, usw.
Aus Performancegründen kann man ja dann auch noch mal die Operatoren entsprechend überladen. Ja, das wäre allerdings viel Arbeit.Der Ritter des size_t Typs wird dir sicher auch noch einiges erzählen.
Außerdem fehlen einige komfortable Funktionen. Ich denke da an trim(), endsWith(String), startsWith(String), substring(), ...
-
@Code-Hacker: interessante idee. nutzt die Klasse das wirklich irgendwo? muss mal suchen.
Bisher nichts gefunden, wird immer neuer Speicher angefordert.
-
Hi!
@Erhard Henkes:
Von std::string wird vom Indexoperator auch eine Referenz zurückgeliefert.
Ja, das nutzt sie, wie z.B. bei replace, wenn der neue Stringteil größer ist als der alte Stringteil und zusätzlich über die 15 Zeichen hinausgeht.EDIT: Sorry, falsch verstanden. Aber ja das tut sie. In der insert-Funktion im else if-Zweig. Sollte if ergeben das len+slen+1>bufsize ist dann wird in den else-Zweig gesprungen.
Code-Hacker
-
Das mit dem char als Rückgabewert des Index-Operators habe ich irgendwann aus dem Buch von Till Jeske C++ nitty-gritty übernommen. Offensichtlich nicht komfortabel.
Die Referenz erlaubt die Änderung des Zeichens (bei Breymann steht es richtig).
Interessante Klasse. Puristisch gefällt mir das mit dem FWDBUFFER noch nicht sonderlich, bringt aber vielleicht deutlich Tempo.
Hat jemand ein Test-Programm für eine String-Klasse, mit dem man testen kann, ob EString performanter als std::string ist?
-
int main() { string str; EString estr; const int NMAX = 15; int max; cout << "Anzahl Schleifendurchlaeufe? "; cin >> max; clock_t t1,t2; double ts, tm=0; cout << endl; for(int n=0; n<NMAX; ++n) { t1 = clock(); for( int i=0; i<max;i++) { str += "hallo"; //zu messende Aktion 1 } t2 = clock(); ts = (t2-t1)/static_cast<float>(CLOCKS_PER_SEC); // Zeit in Sekunden. cout << "Zeit Aktion 1: " << ts << " sec" << endl; tm += ts; } tm/=NMAX; cout << "Durchschnitts-Zeit bei Aktion 1: " << tm << " sec" << endl; tm=0; cout << endl; for(int n=0; n<NMAX; ++n) { t1 = clock(); for( int i=0; i<max;i++) { estr += "hallo"; //zu messende Aktion 2 } t2 = clock(); ts = (t2-t1)/static_cast<float>(CLOCKS_PER_SEC); cout << "Zeit bei Aktion 2: " << ts << " sec" << endl; tm += ts; } tm/=NMAX; cout << "Durchschnitts-Zeit bei Aktion 2: " << tm << " sec" << endl; getch(); }
Anzahl Schleifendurchlaeufe? 10000
Zeit Aktion 1: 0 sec
Zeit Aktion 1: 0 sec
Zeit Aktion 1: 0 sec
Zeit Aktion 1: 0 sec
Zeit Aktion 1: 0.01 sec
Zeit Aktion 1: 0 sec
Zeit Aktion 1: 0 sec
Zeit Aktion 1: 0 sec
Zeit Aktion 1: 0.011 sec
Zeit Aktion 1: 0 sec
Zeit Aktion 1: 0 sec
Zeit Aktion 1: 0.01 sec
Zeit Aktion 1: 0 sec
Zeit Aktion 1: 0 sec
Zeit Aktion 1: 0 sec
Durchschnitts-Zeit bei Aktion 1: 0.00206667 secZeit bei Aktion 2: 0.2 sec
Zeit bei Aktion 2: 0.671 sec
Zeit bei Aktion 2: 1.662 sec
Zeit bei Aktion 2: 3.095 sec
Zeit bei Aktion 2: 4.416 sec
Zeit bei Aktion 2: 5.388 sec
Zeit bei Aktion 2: 6.419 sec
Zeit bei Aktion 2: 7.521 sec
Zeit bei Aktion 2: 9.393 sec
Zeit bei Aktion 2: 9.694 sec
Zeit bei Aktion 2: 11.176 sec
Zeit bei Aktion 2: 12.739 sec
Zeit bei Aktion 2: 13.799 sec
Zeit bei Aktion 2: 14.932 sec
Zeit bei Aktion 2: 16.113 sec
Durchschnitts-Zeit bei Aktion 2: 7.81453 secUups! Untauglich.
Muss an der Funktion EString::insert(...) liegen.
-
Erhard Henkes schrieb:
Hat jemand ein Test-Programm für eine String-Klasse, mit dem man testen kann, ob EString performanter als std::string ist?
Für sowas wirst du nie einen aussagekräftigen Test bekommen, denn meistens sind die string Klassen immer auf einen speziellen Fall vorbereitet. So kann zum Beispiel eine Klasse darauf ausgelegt sein besonders kleine Strings zu speichern und allokiert deswegen sehr wenig Speicher.
Eine auf reference counting basierte Klasse wird keine Chance haben, wenn keine gleichen Strings im Programm verwendet werden, usw.
Man müsste also schon SEHR breit gefächerte Testszenarien ausmessen. Für einen Anhaltspunkt kann man natürlich mal eine kleine Messung machen, aber zu genau sollte man die Ergebnisse nicht nehmen.
-
Was sagst du zu meiner ersten Messung? (siehe oben)
-
@code-hacker.
wollte nur mal fragen "wie" du auf die klasse bekommen bist
also auch nua weil ich mal fast die gleiche machen musste in
André Willms ->C++ Programmierung -->Kapitel 12
würd mich halt nua mal interessieren
-
str += 'a'; //zu messende Aktion 1 estr += 'a'; //zu messende Aktion 2
Anzahl Schleifendurchlaeufe? 10000
...
Durchschnitts-Zeit bei Aktion 1: 0.00206667 secZeit bei Aktion 2: 0.2 sec
Zeit bei Aktion 2: 0.671 sec
Zeit bei Aktion 2: 1.662 sec
Zeit bei Aktion 2: 3.095 sec
Zeit bei Aktion 2: 4.416 sec
Zeit bei Aktion 2: 5.388 sec
Zeit bei Aktion 2: 6.419 sec
Zeit bei Aktion 2: 7.521 sec
Zeit bei Aktion 2: 9.393 sec
Zeit bei Aktion 2: 9.694 sec
Zeit bei Aktion 2: 11.176 sec
Zeit bei Aktion 2: 12.739 sec
Zeit bei Aktion 2: 13.799 sec
Zeit bei Aktion 2: 14.932 sec
Zeit bei Aktion 2: 16.113 sec
Durchschnitts-Zeit bei Aktion 2: 7.81453 sec
-
Hi!
@truebool:
Das ist die Klasse aus "GoTo C++ Programmierung" Kapitel 23.@Erhard Henkes:
std::string ist ja noch hier und da optimiert und kann auch je nach Compiler anders implementiert sein und optimiert. Und ich glaube die Klasse EString ist sehr sicher, als das man da sicher einiges weglassen könnte um performance zu gewinnen.Code-Hacker