char array vs. string



  • Aber es ist nicht schwer, selbst etwas zu bauen, sei es eine operator+ überladung oder eine string cat(const string&,int) funktion.
    allerdings bin ich froh, dass mir nicht irgendein komitee eine solch spezielle bedeutung von + aufhetzt, wenn ich mich dazu entscheide, ein operator+(string,int) zu machen, der strings (->nach int konvertiert) addieren kann.
    freie wahl.



  • Ist ein

    string x = "Sie haben " + lexical_cast<string>(money) + " Euro auf dem Konto."
    

    so viel komplizierter, als ein

    String x = "Sie haben " + (int)money + " Euro auf dem Konto."
    

    ?



  • herrlado schrieb:

    Danke!
    Aber was würdet ihr mir empfehlen, als Anfänger zu nutzen? Bin siet ein Paar Monaten dabei.Bisher habe ich immer string benutzt. Also soll ich mit string weitermachen?

    wie oben gesagt: ja, vergiss aber nicht, dir die char* auch anzuschauen, ohne geht nicht. fahr zweigleisig.



  • Helium schrieb:

    Ist ein

    string x = "Sie haben " + lexical_cast<string>(money) + " Euro auf dem Konto."
    

    so viel komplizierter, als ein

    String x = "Sie haben " + (int)money + " Euro auf dem Konto."
    

    ?

    Nein, aber unschöner. Ich frage mich nur immer, warum euch das weh tun würde, wenn das Standardmäßig so gemacht wird, dass man gleich ints und floats anhängen kann?
    Oder was ist so schlimm daran, sich selber eine Klasse zu schreiben / die bestehende Klasse entsprechend zu erweitern? Das war ja mein Vorschlag.

    davie schrieb:

    Aber es ist nicht schwer, selbst etwas zu bauen, sei es eine operator+ überladung oder eine string cat(const string&,int) funktion.
    allerdings bin ich froh, dass mir nicht irgendein komitee eine solch spezielle bedeutung von + aufhetzt, wenn ich mich dazu entscheide, ein operator+(string,int) zu machen, der strings (->nach int konvertiert) addieren kann.
    freie wahl.

    Ach wie schlimm, dann müsstest du halt beim String ne Methode aufrufen, der dir einen int zurückgibt und in die andere Richtung (die sicherlich häufiger gebraucht wird) hast du es automatisch. Aber so wie es jetzt aussieht, geht ja keins von beiden automatisch.



  • Wo wir von Automatismen reden: Hat deine String-Klasse auch einen operator(int)? Gibt's einen Konstruktor Deinstring(int), der nicht explicit ist?



  • Optimizer schrieb:

    Oder was ist so schlimm daran, sich selber eine Klasse zu schreiben / die bestehende Klasse entsprechend zu erweitern? Das war ja mein Vorschlag.

    Und was ist so schlimm daran zB lexical_cast zu verwenden?

    Das Problem mit dem neuschreiben der String-Klasse ist folgendes: es gibt bereits eine standardisierte Klasse die alles kann was du willst (mit Hilfe von lexical_cast) (IMHO sollten kovertierungen sowieso über eine extra funktion laufen - so dass string dann nicht nur int sondern auch Korkodil kann)



  • Ich bin mir nicht sicher, ob ich dich richtig verstehe. Meine Klasse wird von Zeit zu Zeit noch erweitert, im Moment hat sie das:

    class String
    {
    public:
    	// Konstruktoren:
    	String();
    	String(const String &source);
    	String(const TCHAR *text);
    	String(const __int32 int32Bit);
    	String(const __int64 int64Bit);
    	String(const bool value);
    	// Destruktor:
    	~String();
    
    	// Gibt einen Zeiger auf den String zurück.
    	TCHAR* pointer();
    	// Gibt das Zeichen an der aktuellen Position zurück.
    	TCHAR charAt(int position);
    	// Gibt die Länge des Strings zurück.
    	int length();
    	// Vergleicht zwei Strings.
    	bool equals(const String &other) const;
    
    	// Operatoren '+=' zum Anhängen:
    	void operator+= (TCHAR *otherString);
    	void operator+= (const String &otherString);
    	void operator+= (__int32 int32Bit);
    	void operator+= (__int64 int64Bit);
    	void operator+= (bool value);
    
    	// Operatoren '=' zum Zuweisen:
    	void operator= (const String &otherString);
    	void operator= (const TCHAR *text);
    	void operator= (__int32 int32Bit);
    	void operator= (__int64 int64Bit);
    	void operator= (bool value);
    
    private:
    	TCHAR* data;
    
    	inline void copyStringData(const String &source);
    }
    
    inline const String operator+(const String &s1, const TCHAR *s2)[...]
    inline const String operator+(const String &s1, __int32 s2)[...]
    inline const String operator+(const String &s1, __int64 s2)[...]
    inline const String operator+(const String &s1, bool s2)[...]
    inline const String operator+(const String &s1, const String &s2)[...]
    

    (Fließkomma-Zahlen folgen noch)
    Wenn du meinst, es fehlt was wichtiges, dann nur raus damit. 🙂



  • Optimizer schrieb:

    Aber nicht wahr, Hume, es ist ja so unglaublich falsch, eine anständige Klasse zu coden und damit das Problem ein für alle mal aus der Welt zu schaffen.

    Kannst du lesen oder bist du einfach nur daran interessiert rumzumaulen?

    Ich habe nirgends bezweifelt, dass es nicht Situationen gibt, in denen eine eigene Stringklasse sinnvoll sein kann. Ich schrieb "generell" im Sinne von "im Allgemeinen". Es ist einfach eine Verschwendung von Programmierzeit, wenn man grundlos das Rad neu erfindet. Wer eine eigene Stringklasse schreibt (und diese auch noch in seinen Interfaces verwedent), nur weil er eine Zahl bequem an einen String hängen will (dabei alle besseren Möglichkeiten ignoriert), der hat schlicht und einfach keine Ahnung und sollte lieber nur zu Hause in seinem Keller nur für sich programmieren. Punkt. So und jetzt kannst du von mir aus weiter rumheulen.

    HumeSikkins schrieb:

    Zitat:
    Ich denke die große Portabilität ist der größte Vorteil von good ol' char-Arrays.

    Helium schrieb:

    Hä?

    Was willst du mir damit sagen? Habe ich das falsche Wort verwendet? Was ich sagen wollte, hat Irgendwer in seinem Beitrag bereits angedeutet. const char* sind einfach nach wie vor der kleinste gemeinsame Nenner und ich bevorzuge aus diesem Grund in Schnittstellen von Komponenten die häufig (wieder)verwendet werden immer const char*. Aber vielleicht kannst du ja noch mal etwas ausführlicher deine Probleme mit meiner Antwort darlegen.

    PS:
    Desweiteren halte ich std::string für eine ziemlich furchtbare Stringklasse (der Hauptgrund dafür wurde von Bashar bereits angesprochen). Sie ist aber standard und das ist der ganz entscheidene Vorteil.



  • und wie machst du

    string std_greet("hello"), place("world"), newline("\n");
    string greet = std_greet += " " + place += "!" + newline;
    


  • @Optimizer
    Wenn ich mir deine Klasse so anschaue, denke ich, du solltest besser bei Java bleiben.

    Allein die Operatorüberladung der Marke willkür treibt einem, der mal Scott Meyers gelesen hat Tränen in die Augen.

    Wenn du doch bei C++ bleiben möchtest, solltest du auf jeden Fall mal "Effective C++" zur Hand nehmen.



  • Und es ist jetz intuitiv charAt statt des in C++ üblichen operator[] zu verwenden? und es ist intuitiv die Methode equals zu verwenden, statt des in C++ üblichen operator ==?

    Was willst du mir damit sagen? Habe ich das falsche Wort verwendet? Was ich sagen wollte, hat Irgendwer in seinem Beitrag bereits angedeutet. const char* sind einfach nach wie vor der kleinste gemeinsame Nenner und ich bevorzuge aus diesem Grund in Schnittstellen von Komponenten die häufig (wieder)verwendet werden immer const char*. Aber vielleicht kannst du ja noch mal etwas ausführlicher deine Probleme mit meiner Antwort darlegen.

    Genau das meinte ich: In wie fern Portabler? Man braucht char* für Schnittstellen, was es aber doch nicht Portabler macht.



  • HumeSikkins schrieb:

    Optimizer schrieb:

    Aber nicht wahr, Hume, es ist ja so unglaublich falsch, eine anständige Klasse zu coden und damit das Problem ein für alle mal aus der Welt zu schaffen.

    Kannst du lesen oder bist du einfach nur daran interessiert rumzumaulen? Ich schrieb "generell" im Sinne von "im Allgemeinen".

    Super, und ich finde, generell sollte jeder mal ein String-Klasse schreiben. Genauso geile Begründung.
    So wie du es gesagt hast, ist es das schlimmste was man machen kann, wenn man einmal in seinem Leben so ne Klasse schreibt und die immer wieder verwenden kann.

    HumeSikkins schrieb:

    @Optimizer
    Wenn ich mir deine Klasse so anschaue, denke ich, du solltest besser bei Java bleiben.

    Allein die Operatorüberladung der Marke willkür treibt einem, der mal Scott Meyers gelesen hat Tränen in die Augen.

    Wenn du doch bei C++ bleiben möchtest, solltest du auf jeden Fall mal "Effective C++" zur Hand nehmen.

    Mach einen besseren Vorschlag. Ich habe weder gesagt, dass meine Klasse perfekt ist, noch dass ich übermäßig stolz auf sie bin. Ich habe sie geschrieben, um bequem Dateien in einer Schleife laden zu können (siehe weiter oben).
    Und wenn du was zu verbessern hast, dann lass es doch bitte hören.

    davie schrieb:

    und wie machst du

    string std_greet("hello"), place("world"), newline("\n");
    string greet = std_greet += " " + place += "!" + newline;
    

    Was beabsichtigst du genau? Vielleicht meinst du ja:

    String greet("hello"), place("world"), newLine("\n");
    String bla = greet + place + newLine;
    

    Helium schrieb:

    Und es ist jetz intuitiv charAt statt des in C++ üblichen operator[] zu verwenden? und es ist intuitiv die Methode equals zu verwenden, statt des in C++ üblichen operator ==?

    charAt() finde ich jetzt ziemlich intuitiv. Den Operator '==' wollte ich auch noch überladen, der soll dann die Methode equals() aufrufen. Dagegen ist ja nichts einzuwenden.



  • Ich persönlich sehe auch keinen Grund sich die Arbeit zu machen eine neue stringklasse zu schreiben.
    Ich wüsste auch nicht was ich davon habe ? Ne menge Zeitverlust. Und das in der ohnehin knapp bemessenen Zeit. Lieber erst anschauen wenn man es
    wirklich braucht.

    Wie bereits vorgestellt, überladen von operator<< als template hilft weiter. Ich selbst habe damit eine ini Klasse geschrieben um allerlei Datentypen wegschreiben zu können. Sollte man dann mal an einen Punkt kommen wo der Profiler aufheult vor schmerz kann man immernoch den operator für einen speziellen Typen spezialisieren und flott integrieren.

    Ich denke der Standard liefert massig Möglichkeiten. Ansonsten gibt es ja bereits erstellte Libs wie looki oder boost. Bevor ich also ewigkeiten in eine neue Klasse stecke bring ich lieber mal boost ans laufen.

    Zu der Sache:

    foo(char *);

    und string übergeben:

    Ich würde hier keinen Buffer kopieren. Meist kann man sich sicher sein das der String nur gelesen wird. Bevor ich also kopiere schliesse ich mich dem unsauberen design an
    und verspreche dem Compiler mit einem const_cast<char*>(string.c_str()); das nichts passiert. Nur in seltenen Fällen kopiere ich um.

    Ich denke jeder zieht erst mal den Standard vor . Erst wenn der Profiler den flaschenhals bei einer Standardmethode aufweisst macht man sich die mühe zu suchen.
    (Bei einer Ini Klasse ist z.B. i.d.r. nichts Zeitkritisches vorhanden...)

    Letzendlich darf man nicht vergessen das Entwicklungszeit mehr Kostet als Rechenzeit. Es bringt also nichts wenn man die schnellste Klasse der Welt hat, wenn keiner die Schnittstelle kennt. In der realität läuft vieles nicht ideal weil es einfach zu teuer wäre.



  • Ich stimme dir in den meisten Punkten zu. Ich habe die Klasse auch nicht zum Spaß geschrieben, sondern ich fand es einfach nur ZU arm, dass ich nicht mal nen einfachen Text mit variablen Werten darin erstellen konnte. Ich habe mich einfach maßlos über die Standard-stringklasse geärgert.
    Da meine Ausgabe nicht über cout funktioniert, sondern über Direct3D, war ich gezwungen, mir eine Möglichkeit zu suchen, ein passendes char-Array zusammenzubasteln.
    Ich habe mir dann eine ganze Weile mit stringstreams geholfen, aber irgendwann war mir das Ganze einfach zu hässlich.
    Zur Entwicklungszeit möchte ich sagen, dass das Ganze nicht länger als (insgesamt!) 5-6 Stunden gedauert hat und diese Zeit gewiss nicht verschwendet war.

    Mir wäre es auch lieber gewesen, std::string würde wenigstens so einfache Verknüpfungen (die trotz aller gegenteiligen Behauptungen kein hoch spezieller Wunsch meinerseits sind) unterstützen. Dann hätte ich mir den Aufwand auch sparen können, wie in anderen Programmiersprachen. Aber ja, es könnte ja sein, dass jemand bei <text> + <zahl> dann ne Verknüpfung <(zahl)text> + <zahl> = <zahl> haben möchte - lol.

    Das könnt ihr sehen wie ihr wollt, ich rate lediglich, std::string zu benutzen und wenn das nicht reicht, eine eigene Klasse zu schreiben (das war der Ausgangspunkt der Diskussion). Das ist ein einmaliger Aufwand.



  • Optimizer schrieb:

    Da meine Ausgabe nicht über cout funktioniert, sondern über Direct3D, war ich gezwungen, mir eine Möglichkeit zu suchen, ein passendes char-Array zusammenzubasteln.

    Alternativ könnte man auch einfach einen Wrapper um die dumme DirectX-Funktion basteln.



  • Optimizer schrieb:

    Mir wäre es auch lieber gewesen, std::string würde wenigstens so einfache Verknüpfungen (die trotz aller gegenteiligen Behauptungen kein hoch spezieller Wunsch meinerseits sind) unterstützen.

    nimm lexical_cast oder ähnliches - schreibs dir einfach selber:

    template<typename To, typename From>
    To convert(From const& from)
    {
      stringstream stream;
      stream<<from;
      To to;
      stream>>to;
      return to;
    }
    

    und schon kannst du
    string s = convert<string>(3);
    schreiben...

    sicher, es ist etwas langsamer, da der std Ctor aufgerufen werden muss - dafür hast du eine sau schneller string klasse (du hast scheinbar keine ahnung was da für jahrelange optimierungen drinnen stecken).

    Was stört dich an dem Convert?

    Das könnt ihr sehen wie ihr wollt, ich rate lediglich, std::string zu benutzen und wenn das nicht reicht, eine eigene Klasse zu schreiben (das war der Ausgangspunkt der Diskussion). Das ist ein einmaliger Aufwand.

    dann sollte man wohl eher zB flex_string von Alexandrescu verwenden 🙂
    Ne - also dieser einmalige Aufwand stimmt natürlich - aber warum die jahrelange Erfahrung der StdLibrary Implementierer (Implementatoren??) wegwerfen?

    Und durch eine (sorry, aber es stimmt) primitive Klasse ersetzen?
    Sicher, sie kann Zahlen verwalten, aber was ist mit neuen Zahlen? zB einen int128, int256,... und was ist mit krokodil und pinguin (um das Java Beispiel zu erweitern). Kannst ja einfach templates verwenden:

    template<typename Other>
    void string::append(Other const& other)
    {
      append(convert<string, Other>(other));
    }
    

    und schon hast du die selbe funktionalität 🙂

    somit könntest du wenigstens eine konvertierungen sinnvoll machen und nicht dauernd überladen...

    oder aber funktionen um dies für std::string zu ermöglichen.

    einziges Problem das ich sehe - sind so sachen wie GetWindowText, die einen buffer erwarten.

    Da schreibe ich aber immer einen wrapper - das ist natürlich etwas langsamer aber dafür kann man auch exception verwenden 🙂

    Für deinen Fall wäre vielleicht ein vector<char> besser?



  • // Operatoren '+=' zum Anhängen: 
        void operator+= (TCHAR *otherString); 
        void operator+= (const String &otherString); 
        void operator+= (__int32 int32Bit); 
        void operator+= (__int64 int64Bit); 
        void operator+= (bool value);
    

    Und was machst Du wenn der Anwender der Klasse einen std::string anfügen will ? Oder sogar eine eigene Klasse ?

    Ich verstehe halt nicht was dagegen spricht mit stringstream einen template operator<< und >> in einem namespace zu schreiben.

    Ansonsten sehe ich das so:

    Ich als einzelner Entwickler kann in 6h nicht das erschaffen was viele wesentlich fittere Leute über eine lange Zeit entwickeln.
    Es spricht ja generell nichts dagegen eine eigene Klasse zu schreiben. (Hab mir ja selbst die mühe gemacht eine Klasse zu schreiben die mit ini Dateien inkl. Kommentaren templates und verschatelten Inis klar kommt.... Inkl. Editor etc. Hab übrigens wesentlich länger als 6 Tage drann geknappert. Bin halt langsam 🤡.

    Generell sollte man verwenden was da ist und erst auf eigene Klassen umsteigen wenn es der Profiler zeigt oder die Anforderung an die Klasse wesentlich höher ist als die Klasse das leisten kann.

    Generell zu sagen: Bau eine eigene Klasse, erst danach verwende die Standardklassen finde ich schlichtweg falsch. Ich denke Du hättest einfacher und sicherer zu Deinem Ziel kommen können 🤡

    Aber irgendwie drehen wir uns im Kreis, die Aussagen sind immer wieder die gleichen 😕



  • DrGreenthumb schrieb:

    Sie taugen nicht als buffer. Wenn zum Beispiel die WinAPI einen string zu rückgeben will muss man zuerst einen char Array erstellen anschließend eine string Kopie anfertigen und dann Array wieder löschen. Wäre es nicht einfacher wenn man reserve aufrufen könnte und anschließend direkt in den Buffer schreiben könnte?

    Wie soll das gescheit funktionieren, dass eine Methode einen char* auf die internen Daten zurückgibt.
    So?

    str.erlaube_reinschreiben(100);
    schreib_rein(str.pointer());
    str.fertig_mit_schreiben();
    

    Dann kommt auf einmal irgend'ne Lib die einen, mit malloc() reservierten, Speicherbereich zurückgibt. Soll string dafür dann auch eine Methode haben, die danach wieder free() aufruft? Bashar hat das schon gut auf den Punkt gebracht.

    Es ist halt der C Weg und da C++ ja C compatible sein soll wäre das mit dem buffer schon schön. Aber notfals kann man das immer noch mit Vererbung hinzufügen.

    Das mit malloc und free wird auch nicht vorkommen weil die meisten Libs in eine Dll gepackt sind/werden können.

    Also meist löse ich das Problem so:

    class Str:public string{
        char*buf;
    public:
        Str():buf(0){}
        ~Str(){delete buf;}
    
        //inherit operator=
        template<class T>
        Str&operator=(T t){assign(t);return *this;}
    
        void set_size(const int i){delete buf;buf=new char[i+1];buf[0]=0;}
        void refresh(){assign(buf);delete buf;buf=0;}
    
        operator const char*(){return c_str();}
        operator char*(){return buf;}
    };
    

    Mir wäre es auch lieber gewesen, std::string würde wenigstens so einfache Verknüpfungen (die trotz aller gegenteiligen Behauptungen kein hoch spezieller Wunsch meinerseits sind) unterstützen.

    Bau std::string doch einfach aus. Wieso das ganze Rad neu erfinden wenn du nur einen Teil neu erfinden willst/musst?

    Alternativ könnte man auch einfach einen Wrapper um die dumme DirectX-Funktion basteln.

    Kommt darauf an wieviel dumme Funktionen es gibt. Sind es viele kann es schneller sein eine eigene string class zu schreiben oder die standard string class zu erweitern.



  • Für die kompatibilität zu C APIs gibt es aber vector!
    Den kann man als Buffer verwenden...



  • Irgendwer schrieb:

    Also meist löse ich das Problem so:
    ...

    Ja, dachte ich schon. Aber dann refresh() aufzurufen, statt delete tmp; ist irgendwie das selbe in grün.

    Kommt darauf an wieviel dumme Funktionen es gibt. Sind es viele kann es schneller sein eine eigene string class zu schreiben oder die standard string class zu erweitern.

    Weiß nicht, ob das aber auch besser ist. So einen Funktionsaufruf möchte ich sowieso nicht "frei" im Code haben. Lieber einen Wrapper rum, mit nur den wichtigen Parametern und gleich auch const string statt char*.


Anmelden zum Antworten