Set- und Getterfunktionen verwenden oder nicht?
-
theliquidwave schrieb:
Kommen wir also wieder zur Geschwindigkeit. Es MUSS doch so sein dass std::string langsamer ist als C-Strings oder nicht?! Gibt es da nicht außerdem nen fetten Overhead wegen Templates, usw...? Ich benutze nämlich verdammt viele Strings

Gruß
Und wo ist deiner Meinung nach der Zusammenhang zwischen Templates und Geschwindigkeit?
-
Der "fette" Template-Overhead macht sich höchstens zur Kompilierzeit bemerkbar, aber da müsstest du schon große Metaprogramme gebaut haben, um das zu merken. std::string ist für die allermeisten Fälle schnell und klein genug. In der Standardbibliothek brauchst du keine Performancebremsen zu suchen.

-
theliquidwave schrieb:
Es MUSS doch so sein dass std::string langsamer ist als C-Strings oder nicht?!
Sag mal, warum du glaubst, dass
std::stringlangsamer ist als eine äquivalente Lösung mit C-Strings (eine, welche die selbe Flexibilität bietet).
-
Hi.
Ok - das mögen Denkfehler sein aber irgendwie geht das aus meinem Hirn nicht so recht raus. Ich denke mir immer, dass ich in den meisten Fällen eh nur strcpy, strstr, strcat und strcmp benutze. Die std::string-Klasse bietet allerdings viel viel mehr Funktionen die ich nie brauche. Und die Funktionen, die ich nicht brauche, werden pro Instanz dann doch als Overhead erstellt obwohl sie es gar nicht müssten.Ein anderer Grund für mich ist es, dass das SDK das ich benutze (Source SDK von VALVe) ebenfalls NUR mit char* (bzw. const char*) arbeitet. Eine ewige hin- und her-konvertiererei zwischen std::string und char* bleibt mir somit erspart.
Edit: Beispiel. Wie sollte man das mit std::string machen?
//Prototype der SDK-Funktion void getModDir(char **pszDir); //Aufruf mit C-String getModDir(&pszString); //Aufruf mit std::string getModDir(&stdString); // ??Gruß
-
theliquidwave schrieb:
Ok - das mögen Denkfehler sein aber irgendwie geht das aus meinem Hirn nicht so recht raus. Ich denke mir immer, dass ich in den meisten Fällen eh nur strcpy, strstr, strcat und strcmp benutze. Die std::string-Klasse bietet allerdings viel viel mehr Funktionen die ich nie brauche. Und die Funktionen, die ich nicht brauche, werden pro Instanz dann doch als Overhead erstellt obwohl sie es gar nicht müssten.
Ohje, völlig falsch. Die Funktionen werden nicht in den Instanzen abgelegt. Im Wesentlichen ist ein Aufruf wie meinString.length() nur eine andere Schreibweise für so etwas wie string_length(&meinString).
Ein klein wenig Overhead gibt es bei virtuellen Funktionen, aber das brauch ich dir hier nicht zu erzählen, string hat nämlich nur nicht-virtuelle Funktionen.Ein anderer Grund für mich ist es, dass das SDK das ich benutze (Source SDK von VALVe) ebenfalls NUR mit char* (bzw. const char*) arbeitet. Eine ewige hin- und her-konvertiererei zwischen std::string und char* bleibt mir somit erspart.
Das kann ein Grund sein. Der richtige Weg wäre vielleicht eine für diesen Fall angepasste String-Klasse zu verwenden.
-
theliquidwave schrieb:
Hi.
Ok - das mögen Denkfehler sein aber irgendwie geht das aus meinem Hirn nicht so recht raus. Ich denke mir immer, dass ich in den meisten Fällen eh nur strcpy, strstr, strcat und strcmp benutze. Die std::string-Klasse bietet allerdings viel viel mehr Funktionen die ich nie brauche. Und die Funktionen, die ich nicht brauche, werden pro Instanz dann doch als Overhead erstellt obwohl sie es gar nicht müssten.Nein. std::string speichert als "Overhead" nur die Länge. Das ist 1 int mehr als bei char*. Dafür braucht's ein char weniger, weil die Nullterminierung entfällt. Dafür laufen dann auch Funktionen die die Länge benötigen schneller.
Ein anderer Grund für mich ist es, dass das SDK das ich benutze (Source SDK von VALVe) ebenfalls NUR mit char* (bzw. const char*) arbeitet. Eine ewige hin- und her-konvertiererei zwischen std::string und char* bleibt mir somit erspart.
Edit: Beispiel. Wie sollte man das mit std::string machen?
//Prototype der SDK-Funktion void getModDir(char **pszDir); //Aufruf mit C-String getModDir(&pszString); //Aufruf mit std::string getModDir(&stdString); // ??
-
SeppJ schrieb:
Edit: Beispiel. Wie sollte man das mit std::string machen?
//Prototype der SDK-Funktion void getModDir(char **pszDir); //Aufruf mit C-String getModDir(&pszString); //Aufruf mit std::string getModDir(&stdString); // ??getModDir(&meinString.c_str());? Nicht so prickelnd. Man hat hier keine Chance, als den String in einen std::string umzukopieren.
-
Bashar schrieb:
theliquidwave schrieb:
Ok - das mögen Denkfehler sein aber irgendwie geht das aus meinem Hirn nicht so recht raus. Ich denke mir immer, dass ich in den meisten Fällen eh nur strcpy, strstr, strcat und strcmp benutze. Die std::string-Klasse bietet allerdings viel viel mehr Funktionen die ich nie brauche. Und die Funktionen, die ich nicht brauche, werden pro Instanz dann doch als Overhead erstellt obwohl sie es gar nicht müssten.
Ohje, völlig falsch. Die Funktionen werden nicht in den Instanzen abgelegt. Im Wesentlichen ist ein Aufruf wie meinString.length() nur eine andere Schreibweise für so etwas wie string_length(&meinString).
Und wenn wir zu einem C-String gehen, dann muss für das count richtig was gemacht werden und nicht nur ein Wert zurückgegeben werden. Also muss man sich einfach überlegen, was man eher braucht. Speicher oder Speed beim zählen..
Einen C-String erhälst du, wie auch schon genannt mit der Memberfunktion
c_str().Ich bezweifle aber stark, dass es bei dir wirklich einen Performance Unterschied macht, ob du
std::stringoder einen C-String benutzt..
-
theliquidwave schrieb:
Und die Funktionen, die ich nicht brauche, werden pro Instanz dann doch als Overhead erstellt obwohl sie es gar nicht müssten.
Genau hier liegt dein Denkfehler. Funktionen werden weder pro Instanz erstellt, noch werden sie gelinkt, wenn sie nicht gebraucht werden. Bei Templates werden sie nicht mal kompiliert.
theliquidwave schrieb:
Ein anderer Grund für mich ist es, dass das SDK das ich benutze (Source SDK von VALVe) ebenfalls NUR mit char* (bzw. const char*) arbeitet. Eine ewige hin- und her-konvertiererei zwischen std::string und char* bleibt mir somit erspart.
Du meinst, weil andere Bibliotheken kein modernes C++ benutzen, ist das ein Grund, es selbst nicht zu tun? Die Konvertierung ist ja wirklich sehr einfach (Konstruktor oder
c_str()). Wenn du hingegen einchar-Array kopieren willst, musst du 1. die Grösse der Quelle bestimmen, 2. Speicher anfordern, 3. Zeichen für Zeichen rüberkopieren und 4. wieder daran denken, den Speicher nach der Benutzung freizugeben. Das kann schnell mühsam werden, wenn eine Funktion zum Beispiel einen String zurückgeben soll und nicht mehr klar ist, wer für die Freigabe verantwortlich ist. Oder falls Exceptions im Spiel sind. Glaub mir, du handelst dir damit Unmengen Probleme aus C ein, welche durch C++-Techniken wie RAII beseitigt werden.theliquidwave schrieb:
Edit: Beispiel. Wie sollte man das mit std::string machen?
//Prototype der SDK-Funktion void getModDir(char **pszDir);Das geht nicht direkt, weil du einen Zeiger auf einen Zeiger (übrigens auch etwas, das man in C++ fast nie braucht) hast. Aber du hast Glück, dass dieses Problem schon mal aufgetaucht ist und ich den Thread wiedergefunden habe. Hier wäre eine Klasse, die das kann.
-
Die verlinkte Klasse ist unter Umständen nicht ideal, da sie für Konvertierungen jeweils umkopiert. Sie ist darauf ausgelegt, dass man selbst einen Container mit Strings (in jenem Fall
std::vector<std::string>) besitzt, der selten einer Schnittstelle mitchar**genügen muss.Allerdings habe ich in diesem Thread auch geschrieben, dass die Klasse bei Bedarf erweitert werden sollte. Sie bietet bereits ein Fundament, um Konvertierungen zu ermöglichen. Weitere Funktionalität muss man sich selbst implementieren, was aber aufgrund des unterliegenden STL-Containers nicht viel mehr Aufwand als ein Funktionsaufruf bedeutet.
Das Wichtigste ist meiner Meinung nach – egal, was für eine Lösung du schlussendlich benutzt – dass fehleranfällige Low-Level-Operationen wie manuelle Speicherverwaltung innerhalb einer Klasse gekapselt sind, sodass der Benutzer sich wirklich wichtigen Dingen zuwenden kann.
-
Festzuhalten bleibt in jedem Fall, dass es Fälle gibt, in denen std::string nicht die ideale Lösung ist. Genau das sind die Fälle, in denen in der Praxis Pointer auf C-Strings oder schlechte Eigenbaulösungen verwendet werden.
Oder vielleicht auch gute Eigenbaulösungen, der Vollständigkeit halber. Eigentlich kann es sowas kaum geben, weil man idealerweise ein std::string-kompatibles Interface haben möchte.
-
void MyClass::parse(const std::string& fileName) { std::ifstream stream( fileName.c_str() ); //// } MyClass class; class.parse( "Hello.txt" );ist einer der Fälle, in denen man leicht auf std::string verzichten kann, denn es wird ein std::string-Objekt erzeugt, nur um auf den c_str zuzugreifen.
Sobald aber die parse-Funktion den fileName nicht direkt verwendet, sondern z.B. in einem Daten-Verzeichnis nach einer Datei mit dem Namen sucht und zur Pfad-Zusammensetzung string-Operationen durchgeführt werden, kann sich der std::string schon wieder lohnen.Also, const char* nicht gleich verteufeln, es gibt (eben auch in der Standard-Bibliothek) genügend Verwendungsmöglichkeiten.
-
Ja, klar hier für das elleinige übergeben macht es nicht viel Sinn, aber bereits in C++0x wirds/sollte es dann auch Konstuktoren für
std::stringgeben. Und dann ist es durchaus angenehmer, wenn man gleich einenstd::stringvorliegen hat, weil keine Umwandlungen mehr nötig sind.Klar gibt es Verwendungsmöglichkeiten für C-Strings, aber die sind nicht da, wo man dann einen
std::stringnachbaut.
Obwohl.. Mir fällt kein richtig gutes Argument/Anwendungsgebiet wo C-Stringsstd::stringwirklich überlegen sind ein, abgesehen von alten Schnittstellen.
-
SeppJ schrieb:
Das ist 1 int mehr als bei char*. Dafür braucht's ein char weniger, weil die Nullterminierung entfällt.
Nicht ganz. Das ist 1 size_t mehr, welches auf 64 bit Systemen einem uint64_t entspricht

Also 1 Byte vs 8 Byte.
Wer allerdings bei Stringverkettungen nicht zuviel Performance verlieren möchte, sollte wie bei C Strings den benötigten Speicherbedarf errechnen/schätzen und dann via reserve() genügend reservieren.
void MyClass::parse(const std::string& fileName) { std::ifstream stream( fileName.c_str() ); //// } MyClass class; class.parse( "Hello.txt" );Oh je, solche Dinger sind ECHTE Performancefresser. Mit echte meine ich ECHTE.
Bitte so schreiben:void MyClass::parse(const std::string& fileName) { std::ifstream stream( fileName.c_str() ); //// } MyClass class; static const std::string halloString = "Hello.txt"; class.parse( halloString );std::string Referenzen niemals mit statischen c-strings füttern

mfg~
-
> std::string Referenzen niemals mit statischen c-strings füttern

Und warum bitte?
-
NewSoftzzz schrieb:
std::string Referenzen niemals mit statischen c-strings füttern

Naja. Entweder man lädt eine Datei einmal, und dann spielt es sicher keine Rolle, ob ein temporäres
std::string-Objekt erstellt wird oder ein statisches. Oder man lädt häufig Dateien, wobei aber die Wahrscheinlichkeit gross ist, dass es sich um verschiedene handelt. Und dann ist man mit dem konstanten String wieder aufgeschmissen.Und also bitte, wenn es einem um Performance geht, darf man die Funktion ruhig einen
const char*nehmen lassen. Das ist effizienter als beide Alternativen.NewSoftzzz schrieb:
Oh je, solche Dinger sind ECHTE Performancefresser. Mit echte meine ich ECHTE.
Wenn man eine Million mal pro Sekunde Dateien lädt, vielleicht.
-
Ad aCTa schrieb:
> std::string Referenzen niemals mit statischen c-strings füttern

Und warum bitte?
Weil bei jedem Aufruf der Funktion ein neues string Objekt erzeugt wird (und evtl. auch der c-string kopiert wird?)
Auf jeden Fall ein Minibenchmark der beiden Varianten gegeinander hatte damals nen großen Performanceunterschied gezeigt.
mfg~
-
theliquidwave schrieb:
Noch einmal ausschweifend: Inline-Funktionen werden also vom Compiler direkt in den Code kompiliert? Dann müsste das hier zum Beispiel:
inline CClass *CClassManager::getClass(char *pszName) { CClass *pClass = NULL; // iterieren, etc... return pClass; } CClass *pClass = pClassManager->getClass("aaa");zu folgendem werden (Compilerintern):
CClass *pCompilerOptimizedClass = NULL; // iterieren, etc... CClass *pClass = pCompilerOptimizedClass;Hier sind ein paar wesentliche Stilfehler. Der Parameter sollte entweder vom typ
const char*oderconst std::string&sein. Einchar*impliziert ja, dass ich den String beschreiben kann. Allerdings ohne, dass ich die Länge kenne.Ausserdem sollte ein getter immer const deklariert werden und nie einen nicht-const Zeiger oder nicht-const Referenzen liefern. Besser sind sowieso Referenzen. Zeiger sollten soweit wie möglich vermieden werden.
Zur ursprünglichen Frage möchte ich ergänzen, dass ein Getter und Setter keinen overhead liefern, wenn sie trivial in der Klasse oder inline im Header definiert sind. Dann werden sie grundsätzlich weg optimiert. Du wirst keinen C++-Compiler finden, der das nicht macht.
-
NewSoftzzz schrieb:
Weil bei jedem Aufruf der Funktion ein neues string Objekt erzeugt wird (und evtl. auch der c-string kopiert wird?)
Auf jeden Fall ein Minibenchmark der beiden Varianten gegeinander hatte damals nen großen Performanceunterschied gezeigt.
Gehst du auf meinen Post auch ein oder lieber nicht?

-
Hui.
Erstmal alles verstehen
Ich muss mich morgen dann mal genauer mit der std::string-Klasse befassen. Ich dachte da wohl immer komplett in die falsche Richtung
@ tntnet: Also sollte der Prototyp so:
inline const CClass *CClassManager::getClass(const char *pszName)oder so:
inline CClass *CClassManager::getClass(const char *pszName) constaussehen? Ich habe beides schon des öfteren gesehen und beides funktioniert.
Wo ist der Unterschied und was ist in diesem Fall richtig?Gruß