Was taugt std::string wirklich?
-
Ich hab schon oft gelesen und gehört, dass std::string nicht wirklich gut sein soll. Da ich aber noch wenig Erfahrung habe und das nicht beurteilen kann, möchte ich mal eure Meinungen zu der Klasse hören.
Besonders interessant fänd ich, wenn ihr noch sagen könntet, was an der string Klasse gut oder nicht so gut ist.
BTW: Gibt es auch Anwendungsgebiete wo man mit den rohen char* arbeitet?
Ich bedanke mich schon mal im voraus für eure mühen!
-
also string speichert im gegensatz zu char eine zeichenkette. char nur einzelne zeichen oder? std:: ist soweit ich es weiß nur der namespace...bin aber selbst noch recht neu in der materie. wenns falsch ist bitte verbessern
-
Hallo,
char speichert einzelne Zeichnen, aber char* eine Zeichenkette. Std:: ist in der Tat der Namespace.
Es gibt natürlich auch Gründe, std::string nicht zu benutzen, z. B. wenn man die STL nicht zur Verfügung hat oder nicht benutzen kann (z. B. bei Mikrocontrollern), das das Inkludieren von string schon einiges an Speicherplatz kostet.
Bequemer zu nutzen als char* ist string allemal, aber von den Internas her kenne ich das auch nicht so gut, um beurteilen zu können was man alles besser machen könnte. Allerdings gab es meines Wissens schon einige Threads diesbezüglich hier, vielleicht bringt dich die funktionierende SuFu weiter.
-
Grob gesagt: mit einem char * hast Du einen schnellen und direkten Zugriff auf Deinen String und kannst die schnellen Funktionen der stdio.h verwenden, aber Du musst Dich selber um Stringgroesse, Allocierung und Sreigabe des Speichers etc. kuemmern; die std::string Class covert dieses, verwaltet selber den Speicher und ist einfacher zu handhaben.
-
Aha, und std::string hat keinen schnellen Zugriff? Ist ja eine putzig Vorstellung.
Im Ernst, std::string ist mit std::vector vergleichbar, also ein sequenzieller Container vom Typ char... um genau zu sein verwaltet std::string auch nur einen char-Array, und merkt sich zus. wie groß das Array ist. Definitiv schneller, als erstmal die Null-Terminierung suchen zu müssen. Egal, jedenfalls kenne ich keine Nachteile die std::string mit sich bringt.
std::string::c_str() z.B. liefert den orig. char-Array zurück, nix mit "umwandeln" oder convert (was ist das denn für ein Unfug?).
Wie CarstenJ gesagt hat, sollte man als C++-Programmierer nur dann auf std::string verzichten, wenn ebend auf einem Microcontroller-System um jedes Byte gekämpft wird. Weil std::string natürlich immer ein paar KByte mehr reserviert, um bei String-Verlängerung nicht jedesmal das char-Array umkopieren zu müssen (reallocierung, für die C-Coder unter euch).
Ansonst: auf einem normalen PC würde ich nie im Leben auf std::string verzichten.
-
std::string::data() liefert einen Zeiger auf die verwaltete Zeichenfolge zurück. std::string::c_str() garantiert darüber hinaus, daß sie null-terminiert ist, was die verwaltete Zeichenfolge nicht zwangsweise ist (ich kann mir allerdings kaum eine Implemementierung vorstellen, bei der data() und c_str() sich unterscheiden).
Ansonsten: immer std::string() verwenden. Es gibt wenige Gründe, darauf zu verzichten.
-
Wenn man schlechte Software schreiben will sollte man aber auf std::string verzichten.
-
std::string hat Vorteile, sicher, aber macht Euch mal den "Spass", zwei kl. Prograemmchen im Assembler zu vergleichen:
int main (int argc, char **argv) { char *szString = "ABCDEFGHILKLMNOP"; char cChar; cChar = szString [5]; printf ("%c\n", cChar); return 0; }
und
int main (int argc, char **argv) { std::string szString = "ABCDEFGHILKLMNOP"; char cChar; cChar = szString [5]; printf ("%c\n", cChar); return 0; }
Den Assembler code fuer das erste Programm (nur main):
push %ebp mov %esp,%ebp sub $0x8,%esp and $0xfffffff0,%esp mov $0x0,%eax sub %eax,%esp movl $0x80485c8,0xfffffffc(%ebp) mov 0xfffffffc(%ebp),%eax add $0x5,%eax mov (%eax),%al mov %al,0xfffffffb(%ebp) sub $0x8,%esp movsbl 0xfffffffb(%ebp),%eax push %eax push $0x80485d9 call 80483a0 <_init+0x48> add $0x10,%esp mov $0x0,%eax leave ret
Der Assemblercode fuer das zweite Beispiel (wieder nur main):
push %ebp mov %esp,%ebp push %ebx sub $0x34,%esp and $0xfffffff0,%esp mov $0x0,%eax sub %eax,%esp sub $0x4,%esp sub $0x8,%esp lea 0xffffffd8(%ebp),%eax push %eax call 80484c8 <_init+0x28> add $0xc,%esp lea 0xffffffd8(%ebp),%eax push %eax push $0x8048818 lea 0xffffffe8(%ebp),%eax push %eax call 80484d8 <_init+0x38> add $0x10,%esp jmp 8048679 <main+0x5d> mov %eax,0xffffffcc(%ebp) mov 0xffffffcc(%ebp),%ebx sub $0xc,%esp lea 0xffffffd8(%ebp),%eax push %eax call 8048538 <_init+0x98> add $0x10,%esp mov %ebx,0xffffffcc(%ebp) sub $0xc,%esp pushl 0xffffffcc(%ebp) call 8048548 <_init+0xa8> sub $0xc,%esp lea 0xffffffd8(%ebp),%eax push %eax call 8048538 <_init+0x98> add $0x10,%esp sub $0x8,%esp push $0x5 lea 0xffffffe8(%ebp),%eax push %eax call 80484f8 <_init+0x58> add $0x10,%esp mov (%eax),%al mov %al,0xffffffd7(%ebp) sub $0x8,%esp movsbl 0xffffffd7(%ebp),%eax push %eax push $0x8048829 call 8048528 <_init+0x88> add $0x10,%esp sub $0xc,%esp lea 0xffffffe8(%ebp),%eax push %eax call 80484e8 <_init+0x48> add $0x10,%esp movl $0x0,0xffffffd0(%ebp) jmp 80486ee <main+0xd2> mov %eax,0xffffffcc(%ebp) mov 0xffffffcc(%ebp),%ebx sub $0xc,%esp lea 0xffffffe8(%ebp),%eax push %eax call 80484e8 <_init+0x48> add $0x10,%esp mov %ebx,0xffffffcc(%ebp) sub $0xc,%esp pushl 0xffffffcc(%ebp) call 8048548 <_init+0xa8> mov 0xffffffd0(%ebp),%eax mov 0xfffffffc(%ebp),%ebx
Der Unerschied ist auch vollkommen klar. In einem Falle greife ich direkt auf den Speicher zu, im anderen anderen Falle rufe zunaechst einen new-Operator auf und dann eine ueberladenen Operator, genau genommen am Ende ein kaschierter Methodenaufruf ist. Das muss langsamer sein und Heap verschwenden - vom Stack mal nicht zu reden, denn alle Aufrufe dahinter muessen wieder vom Stack verwaltet werden. Deshalb ist manchmal besser mit dem Pointer auf char zu arbeiten, wenn man jedoch Stack und Heap ausreichend zu verfuegung, sollte man sich ueberlegen, ob man den type-safe std::string verwendet.
-
... schrieb:
Ich hab schon oft gelesen und gehört, dass std::string nicht wirklich gut sein soll.
das ist der fall. std::string ist nicht perfekt. und man soll std::string auch nur nehmen, wenn es deutliche vorteile vor char[] hat. also zu 99% ist std::string zu nehmen (kein witz!).
Da ich aber noch wenig Erfahrung habe und das nicht beurteilen kann, möchte ich mal eure Meinungen zu der Klasse hören.
std::string ist nur zu überbieten, wenn du sauviel erfahrung hast. und selbst dann nicht als allgemeine lösung, sondern immer nur in spezialfällen. es gibt halt nur *ein* std::stirng. man kann sich fein klassen bauen, die schneller concattenieren (vor allem mit expression templates). man kann sich feinklassen bauen, die für zumeinst sehr kurze strings besser sind (union, und daten in den speicher schreiben, wo sonst zeiger sind). man kann sich leicht klassen bauen, die sehr schnell vergleichen, wenn immer wieder die gleichen strings kommen (atomtabelle). ok, es ist nicht gerade leicht. und std::string schlägt man in den seltensten fällen um mehr als 50%.
Besonders interessant fänd ich, wenn ihr noch sagen könntet, was an der string Klasse gut oder nicht so gut ist.
sie funktioniert einfach. ohne fehler.
ich erinnere nur mal an das einlesen von zeilen aus einer datei. string wächst einfach so lange, bis die zeile reinpaßt. kein ärger mit overflows.BTW: Gibt es auch Anwendungsgebiete wo man mit den rohen char* arbeitet?
das betriebssystem nimmt als dateinamen rohe char*. ich wäre ungeschickt, wenn ich konstante dateinamen erst nach string konvertieren würde und dann doch .c_str() aufrufen würde.
als allgemeine regel würde ich sagen: nimm char*, wenn es geht. (also 1% oder so). nimm std::string sonst. bau die eine noch bessere klasse (also für dein konkretes programm bessere klasse), wenn du etwas besser machen kannst, als es std::string macht (wenn ich das recht einschätze, machen das nur leute wie ich und MrN, aber gurus wie marc++us und hume und shade machen das gar nicht). vielleicht solltest du gar nicht drüber nachdenken, was anderes als std::string zu nehmen.
-
es gab da auch mal die geschichte, dass std::string probleme macht wenns in einer klasse als membervariable verwendet wird die aus einer dll exportiert wird
wobei das erzeugen und löschen der klasseninstanzen ausserhalb der dll stattfindet
womit hängt das eigentlich zusammen?
@topic ich benutz std::string garnich... liegt aber daran dass viele embedded systeme keine stl ham. aber ich behaupt auch ned dass meine eigene stringklasse schneller is
-
... schrieb:
Ich hab schon oft gelesen und gehört, dass std::string nicht wirklich gut sein soll.
Nunja, gut ist relativ. Für Otto-Normalverbraucher reicht std::string locker aus. Wer intensiver mit Strings arbeitet, wird sich aber mit Sicherheit schon mal eine handlichere oder funktionsreichere Klasse gewünscht haben.
Ein Problem bei std::string bzw. std::wstring ist zB, dass man diese Klassen nur begrenzt für Unicode nutzen kann. Das liegt einfach daran, dass die Elemente immer gleich gross sind. Bei Unicode, zB UTF-8, haben die Zeichen aber eine variable Länge, je nachdem, wie sie codiert sind. Oder was ist mit Operatoren? Darüber hatten wir schon eine Diskussion und sind letztendlich zu dem Ergebnis gekommen, dass diese nicht immer glücklich gewählt sind. ZBstd::string a = "Hallo"; std::string b = " Welt!"; std::string c = a + b;
Wir schreiben hier a + b, und aus dem Mathematikunterricht wissen wir, dass dies bedeutet, a und b zu addieren. Strings werden aber nicht addiert, höchstens aneinandergehängt oder konkateniert. + ist hier jedenfalls nicht die beste Form die gewollte Aktion auszudrücken. "aaa" + "bbb" kann ja auch bedeuten, als Ergebnis "ccc" zu erhalten.
std::string hat aber auch Vorteile. Wesentlich ist hier, dass zum einen das Handling gegenüber C-Strings und den str... Funktionen einfacher ist. Zum anderen muss ein std::string intern nicht als C-String implementiert sein. Und das hat deutliche Vortiele. ZB hat allein die Längenermittlung von C-Strings eine Komplexität von O(n). Bei einem std::string ist dies konstant und idR ein simples 'return bla'. Also praktisch zu vernachlässigen.Fazit:
Wer intensives String Handling betreibt und enorme Funktionalität benötigt, sollte sich nach Alternativen umschauen. Für alle anderen ist std::string das Mittel der Wahl in C++ und 'char*' praktisch immer vorzuziehen. Allerdings gibts auch hier Ausnahmen von der Regel. ZB reicht für Literale 'const char*' allemal aus. Oder so ist std::string ungeeignet für Parameter in DLL Funktionen.MBCS-CITP schrieb:
Der Unerschied ist auch vollkommen klar. In einem Falle greife ich direkt auf den Speicher zu, im anderen anderen Falle rufe zunaechst einen new-Operator auf
In deinem Beispiel ja, stimmt aber so nicht ganz. Ein std::string verfolgt die Speicherpolitik, die ihm mit dem Allokator mitgegeben wird. Theoretisch sollte es also auch möglich sein, für einen std::string Stack zu verwenden.
MBCS-CITP schrieb:
und dann eine ueberladenen Operator, genau genommen am Ende ein kaschierter Methodenaufruf ist. Das muss langsamer sein und Heap verschwenden - vom Stack mal nicht zu reden, denn alle Aufrufe dahinter muessen wieder vom Stack verwaltet werden.
Nein. Wie du sicher weisst, können Funktionen geinlined werden.
-
std::string sollte in jedem Fall in c++-Programmen verwendet werden. Wenn man es nicht verwendet, dann nur zugunsten anderer String-Klassen, die entweder schneller sind oder weil man irgendwelche Bibs verwendet, die ihre eigene String-Klasse verwenden (wär aber in vielen Fällen besser, diese Bib-spezifischen Klassen wegzukapseln und nur an der Schnittstelle zur Bib zu verwenden). Praktisch nie sollte man char* verwenden um nichtkonstante Strings(!) in C++ darzustellen. Also sobald man irgendwas mit dem String macht (vergleichen, sortieren, kopieren ...) -> kein char*!
std::string wird zwar tatsächlich oft kritisiert, aber die kritik geht nicht dahin, dass man sie nicht verwenden soll, sondern dass sie vom Design her nicht so gut ist, wie sie sein könnte. Meist werden zwei Punkte angesprochen:
- Unicode kann nicht unterstützt werden (hier schon angesprochen) (wobei ich - ohne mich jemals näher damit beschäftigt zu haben das nicht versteh. Wieso kann man das nicht wenigstens über ne Spezialisierung machen???)
- Weil die Klasse eine fette schnittstelle hat. Nach Meinung einiger tonangebenden Gurus, sollte eine Klasse nur die notwendigen Methoden bieten. Alles was darüber hinaus geht und nur aus konfort-Gründen angeboten wird, sollte über non-member-non-friend-Funktionen angeboten werden. (s. http://www.gotw.ca/gotw/084.htm)Zweiteres spricht aber nicht gegen eine Benutzung (man sollte eher daraus für das eigenen Klassendesign lernen), ersteres nur in einem Spezialfall (wenn man Unicode verwendet).
Aussagen, dass std::string inperformant ist, sind prinzipiell mit Vorsicht zu genießen weil:
- Jeder Hersteller kann das Ding anders programmieren -> Performance so pauschal nicht vergleichbar
- Wer kann schon auf die schnelle ne String-Klasse besser implementieren als die hochbezahlten Jungs von Dinkumware, roguewave oder (weniger hochbezahlte) Open-Source-Gurus?@MBCS-CITP
Fairerweise solltest Du wenigstensconst std::string szString ("ABCDEFGHILKLMNOP");
schreiben. Und Code-Optimierung anschalten. Könnte sein, dass es da schon anders aussieht.
es gab da auch mal die geschichte, dass std::string probleme macht wenns in einer klasse als membervariable verwendet wird die aus einer dll exportiert wird
wobei das erzeugen und löschen der klasseninstanzen ausserhalb der dll stattfindet
das liegt schlicht und einfach daran, dass der export von Klassen in einer DLL nicht standartisiert ist. Prinzipiell kann man über ne dll nur C-sachen exportieren, weil das der kleinste gemeinsame Nenner ist. Also Funktionen und POD-Structs, keine Objekte. Wenn man Klassen exportiert, ist man darauf angewießen, dass auf beiden Seiten derselbe Compiler mit derselben Version und denselben Optimierungseinstellungen verwendet wird. Es gibt jetzt Herstellerspezifische Erweiterungen (z.B. in den MFC), die definieren, wie man Klassen doch exportieren kann, das geht aber nur in bestimmten Grenzen.
-
In diesem speziellen Fall ist das Problem, dass dann die DLL und der Client der DLL verschiedene Heaps verwenden. Der string wird am Heap der DLL angelegt und soll dann am Heap des Client zerstört werden. Das kann nicht gehen. Bei VC++ kann man es aber ermöglichen, indem man sicherstellt, dass beide Parteien die dynamische C-Runtime verwenden (msvcrt.dll).
-
groovemaster schrieb:
In deinem Beispiel ja, stimmt aber so nicht ganz. Ein std::string verfolgt die Speicherpolitik, die ihm mit dem Allokator mitgegeben wird. Theoretisch sollte es also auch möglich sein, für einen std::string Stack zu verwenden.
mach mal vor.
mir will nicht in den kopf, wie ich aus einer funktion heraus, mäclich dem ctor von string dann noch speicher vom stack abzocke und der speicher überlebt das funktionsende.
-
std::string _ist_ es.
Bye, TGGC
-
kartoffelsack schrieb:
- Unicode kann nicht unterstützt werden (hier schon angesprochen) (wobei ich - ohne mich jemals näher damit beschäftigt zu haben das nicht versteh. Wieso kann man das nicht wenigstens über ne Spezialisierung machen???)
std::string basiert auf dem STL-Container std::basic_string.
std::string ist die Klasse für "char" Strings.
std::wstring ist die Klasse für "wchar_t" Strings.D.h. für Unicode nimmt man die std::wstring Klasse.
Da im aktuellen C++ Standard die STL genormt ist, müssen alle Implementationen der STL identisch sein (zumindest von der Schnittstelle her).
Die Verwendung von Unicode erfordert sowieso das Studium des Unicode 4.0 Standards. Sonst kann es leicht passieren, daß man etwas übersieht.
Von den Container-basierten und ziemlich kurz gehaltenen String-Klassen der STL sollte man sich nicht zu viel erwarten, da sie nur eine minimale Programmierschnittstelle haben.
Durch Verwendung von std::wstring kann man jedoch viel Zeit sparen beim Behandeln von Unicode Strings.
Es gibt noch die C Library Funktionen für Unicode, mit denen man u.U. Konvertierungen etc. erledigen kann.
Was die Codeerzeugung betrifft: Wie Du bereits gesagt hast, erfordert die Reduktion des Codes einen guten Optimizer.
-
Power Off schrieb:
Von den Container-basierten und ziemlich kurz gehaltenen String-Klassen der STL sollte man sich nicht zu viel erwarten, da sie nur eine minimale Programmierschnittstelle haben.
schau mal ins wörterbuch, was "minimal" heißt und nimm dann diese aussage zurück. die stl-container sind eierlegende wollmilchsäue mit viel zu breiten schnittstellen. oder kannste mir mal erzähln, wozu ne map iteratoren haben sollte? damit man um baum auch aufwärtszeiger hat und pro knoten 4 byte wegschmeißt? ich kann es jedenfalls nicht nachvollziehen und ich brauche auch nie iteratoren von maps.
-
und wie läuft man sonst alle elemente einer map durch?
-
Ich verwende ne map zB in meinen Funktionen zum laden und speichern von INI Dateien und da ist der map-Zugriff per Name im Programm extrem praktisch, zum speichern brauch ich jedoch iteratoren um alle Elemente durchzugehen und abzuspeichern.
-
Power Off schrieb:
kartoffelsack schrieb:
- Unicode kann nicht unterstützt werden (hier schon angesprochen) (wobei ich - ohne mich jemals näher damit beschäftigt zu haben das nicht versteh. Wieso kann man das nicht wenigstens über ne Spezialisierung machen???)
std::string basiert auf dem STL-Container std::basic_string.
std::string ist die Klasse für "char" Strings.
std::wstring ist die Klasse für "wchar_t" Strings.D.h. für Unicode nimmt man die std::wstring Klasse.
Und hat immer noch verloren. wchar_t hat keine feste Größe und keine feste Kodierung. wstring erkennt trotzdem nicht mehr-char-zeichen, wenn du den Indexoperator benutzt. wstring weiß gar nichts, es ist nur ein Buffer mit der Funktionalität, einen anderen Buffer daran zu ketten. Die Speicherverwaltung nimmt der std::string ab, sonst nichts.
Da im aktuellen C++ Standard die STL genormt ist, müssen alle Implementationen der STL identisch sein (zumindest von der Schnittstelle her).
Ja richtig, nur die Schnittstelle.
Die Verwendung von Unicode erfordert sowieso das Studium des Unicode 4.0 Standards. Sonst kann es leicht passieren, daß man etwas übersieht.
Was denn zum Beispiel? Da gibt's nichts zu übersehen, Unicode 4.0 ist eine Tabelle mit Zahlen und Zeichen. Was für den Programmierer wichtiger ist, sind die Transformationsformate.
Von den Container-basierten und ziemlich kurz gehaltenen String-Klassen der STL sollte man sich nicht zu viel erwarten, da sie nur eine minimale Programmierschnittstelle haben.
Minimal ist sie nicht, du hast sogar die Wahl zwischen size() und length().
Durch Verwendung von std::wstring kann man jedoch viel Zeit sparen beim Behandeln von Unicode Strings.
Nein. std::wstring ist komplett unbrauchbar und immer plattform- sowie Compiler-abhängig.