Frage zu C++ Primer Übung 4.29 (Zeichenstrings im C-Stil)



  • hmm.. hab ich noch nich gewusst - und im msvc seh ich da auch nicht durch -.-
    wenn du mal lange weile hast, kannste das ja da auch mal zeigen!? 🤡

    wobei mir das gerade sehr komisch vorkommt, weil dann ja jedes mal ne fallunterscheidung gemacht werden muss (op+=, dtor, ...) - also ob man dann jz kopieren(aufräumen) muss oder ob man schon nen eigenen speicherbereich hat...
    haste lust, das zu erklären? : >

    bb



  • MSVC 6 hat AFAIK noch eine STL dabei die COW für std::string verwendet.
    MSVC 7.1 und 8 haben "normale" std::string Implementierungen, die lediglich die SSO verwenden (small string optimization).

    COW für std::string zu implementieren ist kaum sinnvoll möglich. In single-threaded Applikationen würde es in einigen wenigen Fällen etwas bringen. Im multi-threading Applikationen müsste man für den Referenz-Zähler CAS bzw. InterlockedIncrement/InterlockedDecrement verwenden, was einiges kostet. Idealerweise wäre malloc schonmal schneller als ein einziger CAS/InterlockedXxx Aufruf. Wäre deswegen, weil malloc auf Windows/MSVC ziemlich langsam ist - IIRC ca. 2,5 mal langsamer als CAS (auf heutiger Standard-Hardware mit Intel CPU -- AMD hab' ich nie gemessen).

    2,5 mal klingt jetzt als ob man damit noch gut was rausholen könnte. Ist aber nicht so. Warum? Weil COW bei std::string eben nahezu unbrauchbar ist. So ziemlich das einzige wo bei einem geCOWten std::string nicht kopiert werden muss, ist den String 1:1 durchzureichen, oder maximal noch size() aufzurufen. Sobald man den String aber z.B. über eine mutable Referenz ausliest, muss kopiert werden. Klingt komisch, is aber so. Grund: die String Klasse kann nicht wissen, dass diverse Zugriffe, die eine mutable Referenz auf die im String gespeicherten chars zurückliefern, nicht wirklich verwendet werden um den String zu manipulieren.

    Beispiel:

    #include <iostream>
    #include <string>
    
    int main()
    {
        std::string s1 = "seppdepp";
        std::string s2 = s1; // COW?
    
        char c = s1[5]; // liest bloss, sieht harmlos aus
        char& ref = s1[5]; // hmmm...
        ref = 'X'; // HMMMMM...
    
        std::cout << "s1=" << s1 << "\n";
        std::cout << "s2=" << s2 << "\n";
    }
    

    std::string kann hier nicht unterscheiden ob der operator [] aufgerufen wird um den String auszulesen, oder um ihn zu verändern. Dasselbe Problem hat man beim Dereferenzieren von Iteratoren. Kurz: bei fast jeder Operation muss der String kopiert werden, um sicherzustellen, dass andere Strings nicht "unabsichtlich" geändert werden könnten.

    Zurück zum Thema "ist aber nicht so". COW bringt nix, weil fast jeder String, der über COW "geshared" wird, gleich wieder kopiert werden muss. Wenn man für das "sharen" und "un-sharen" fast nix zahlt, wie eben bei single-threaded Applikationen, kann es trotzdem was bringen, und kaum schaden. Wenn man aber für das sharen & un-sharen ca. 40% des kopierens/freigebens zahlt, dann bringt es eben nixmehr. Bzw. macht die Sache sogar langsamer.



  • noch einfacher: in normalen anwendungen wird jeder kopierte string auch verändert, denn nur dazu hat man ihn kopiert. also bringt copy-on-write nix.

    was ist CAS?



  • CAS = compare and swap = InterlockedCompareExchange

    noch einfacher: in normalen anwendungen wird jeder kopierte string auch verändert, denn nur dazu hat man ihn kopiert. also bringt copy-on-write nix.

    Das würde ich so nicht sagen. An wirklich vielen Stellen werden Strings nur rumgereicht und gelesen. Oft sogar nur durchgereicht. Als Parameter gehts ja noch mit cosnt&, aber doof sind u.a. Returnwerte.

    Das Problem ist wirklich nur, dass das Lesen bei std::string auch dazu führt dass die Kuh kopiert werden muss.

    Der neue Standard wird dank move-semantics da an vielen Stellen helfen.

    Ich wünsche mir aber trotzdem std::const_string 🙂



  • hustbaer schrieb:

    Ich wünsche mir aber trotzdem std::const_string 🙂

    Sowas wie der einmal von Shade of Mine vorgeschlagene NonCopy-String?

    Ist wahrscheinlich nicht allzu schwierig, sowas zu implementieren... Aber wenns grad im Standard wäre, wäre das natürlich nichts schlecht. 😉



  • Nexus schrieb:

    hustbaer schrieb:

    Ich wünsche mir aber trotzdem std::const_string 🙂

    Sowas wie der einmal von Shade of Mine vorgeschlagene NonCopy-String?

    Nö, ein sehrwohl-copy String der aber einfach immutable ist. Der könnte dann nämlich sharen soviel er mag.

    Ist wahrscheinlich nicht allzu schwierig, sowas zu implementieren... Aber wenns grad im Standard wäre, wäre das natürlich nichts schlecht. 😉

    Jo, solange es nicht in Standard ist ists ziemlich zwecklos.



  • hustbaer schrieb:

    Nexus schrieb:

    hustbaer schrieb:

    Ich wünsche mir aber trotzdem std::const_string 🙂

    Sowas wie der einmal von Shade of Mine vorgeschlagene NonCopy-String?

    Nö, ein sehrwohl-copy String der aber einfach immutable ist. Der könnte dann nämlich sharen soviel er mag.

    reicht es da nicht einfach schon,

    const string appName(string("foo")+bar);
    

    zu schreiben.
    der kluge bibliotheksentwickler hat doch sicherlich dran gedacht, daß die

    char& operator[](size_t index)
    

    eine kopie erzeugt, aber die

    char const& operator[](size_t index) const
    

    sowas nicht macht.



  • hustbaer schrieb:

    Nö, ein sehrwohl-copy String der aber einfach immutable ist. Der könnte dann nämlich sharen soviel er mag.

    Aber käme man da nicht bereits mit const char* relativ weit? Vielleicht noch leicht gewrappt, dass man sich nicht mehr um die Speicherverwaltung kümmern muss...

    hustbaer schrieb:

    Jo, solange es nicht in Standard ist ists ziemlich zwecklos.

    Ja, es ist dann eben nicht "anerkannt" und die Verbreitung würde sich im Mass halten. Aber innerhalb der eigenen Projekte spricht ja nichts dagegen, sich zusätzliche Utility-Klassen zu basteln...



  • Nexus schrieb:

    hustbaer schrieb:

    Nö, ein sehrwohl-copy String der aber einfach immutable ist. Der könnte dann nämlich sharen soviel er mag.

    Aber käme man da nicht bereits mit const char* relativ weit? Vielleicht noch leicht gewrappt, dass man sich nicht mehr um die Speicherverwaltung kümmern muss...

    ja, wrapper, referenzzähler rund innendrin ein char const*, voila, ein const_string.



  • volkard schrieb:

    ja, wrapper, referenzzähler rund innendrin ein char const*, voila, ein const_string.

    boost::shared_array<const char> ? 😉


Anmelden zum Antworten