[gelöst] komisches verhalten von std::vector<std::string>, g++ 4.4.1



  • Hallo!
    ich weiß, makros sind der teufel, aber trotzdem habe ich mal folgenden test durchgeführt:

    #include <vector>
    #include <iostream>
    #include <string>
    
    #define __REGISTER(A) ans.push_back(1.0);\
                        ansnames.push_back(std::string(#A));\
                        double* out_##A=&ans.back();
    
    int main(int argc, char *argv[])
    {
    
        std::vector<double> ans;
        std::vector<std::string> ansnames;
    
        __REGISTER(hans1);
        __REGISTER(hans2);
        __REGISTER(hans3);
        __REGISTER(hans4);
        __REGISTER(hans5);
        __REGISTER(hans6);
        __REGISTER(hans7);
        __REGISTER(hans8);
        __REGISTER(hans9);
        __REGISTER(fritz);
        __REGISTER(franz);
    
        *out_hans1=10;
        *out_hans2=20;
        *out_hans3=30;
        std::cout<<*out_hans1<<std::endl;
        std::cout<<*out_hans2<<std::endl;
        std::cout<<*out_hans3<<std::endl;
        for(int i=0;i<ansnames.size();++i)
            std::cout<<ansnames[i]<<std::endl;
        return 0;
    }
    

    es wird also von dem makro folgendes ausgeführt:

    ans.push_back(1.0); ansnames.push_back(std::string("hans1")); double* out_hans1=&ans.back();
    

    habe ich auch per g++ -E main.cpp verifiziert, sollte also funktionieren... hier aber die ausgabe:

    Starting /home/thomas/programmierung/tests/makrotest2/makrotest2...
    10
    20
    30
    hans1
    hans2
    4@3
    hans4
    hans5
    hans6
    hans7
    hans8
    hans9
    fritz
    franz
    /home/thomas/programmierung/tests/makrotest2/makrotest2 exited with code 0
    

    verwende ich einen anderen datentyp für die strings, wie z.B. QString, betrifft es einen anderen string in der liste, z.B. erst den fünften. Kann mir das jmd erklären?
    Kann das damit zu tun haben, dass std::vector sizeof(std::string) zur compile-zeit wissen will?

    danke,

    thomas



  • Es ist bei std:vector nicht garantiert, dass nach dem Einfuegen oder Loeschen von Elementen Iteratoren auf irgendwelche Elementen noch gueltig sind.



  • wenn er die Elemente im Heap anlegt müsste es eigentlich gehen... solange er die Adressen noch kennt.



  • ich benutze ja auch keine iteratoren, die ich vor dem einfügen von elementen definiert habe - die reihenfolge ist ja .push_back(blubb) ... zugriff per [] operator - oder verstehe ich da was falsch?



  • *out_hans1
    

    Hier dereferenzierst du einen Zeiger (Iteratoren sind bei Containern nur 'ne Abstraktion davon). Der Container hat seine Groesse veraendert, also muss hans1 nicht mehr zwingend an dieser Stellen liegen.



  • ja, der zeigt aber auf ein element aus der liste ans, also auf einen double-wert. sorgen bereiten mir die elemente der string-liste ansnames.


  • Mod

    Dein Problem wurde dir ja schon erklärt. Außerdem will ich noch hinzufügen, dass Bezeichner mit zwei Unterstrichen oder einem Unterstrich am Anfang für Compilerimplementierungen reserviert sind und nicht verwendet werden sollten.



  • @SeppJ: Danke, werde ich in Zukunft berücksichtigen.



  • gahre schrieb:

    ja, der zeigt aber auf ein element aus der liste ans, also auf einen double-wert. sorgen bereiten mir die elemente der string-liste ansnames.

    Nein tut er nicht, an dieser Stelle kann sich jetzt was anderes befinden. Da sich ansnames von der Groesse geandert hat, koennen sich dort auch Teile von ansnames befinden. Wenn man mit Containern arbeitet, sollte man auch in erster Linie Iteratoren bevorzugen. Es sind einfach die C++-Sprachmittel. Abschliessend: Es besteht ein unterschied zwischen einer Liste (std::list) und einem Array (std::vector).


  • Mod

    gahre schrieb:

    ja, der zeigt aber auf ein element aus der liste ans

    Eben nicht. Die Liste liegt nach dem Einfügen der Elemente ganz woanders, dein Iterator (oder Zeiger, wenn du es so nennen willst) zeigt aber immer noch auf die alte Stelle. Als ich dein Programm ausgeführt habe, habe ich beispielsweise einen Fehler bekommen, dass der Stack zerschossen wäre. Soclhe Fehler fallen natürlich erst auf, wenn die zerstörte Stelle des Stacks benutzt wird, was sehr viel später sein kann. Nimm die Zeilen mal raus und du wirst sehen, es wird fehlerlos funktionieren.



  • ah alles klar. ich stand kurz aufm schlauch. indem ich

    *out_hans#=10;
    

    oder ähnliches mache, ändere ich eventuell einen bereich, in dem ans[#] gar nicht mehr liegt, sondern ein teil von meiner stringliste...
    und auch wenn ich das makro ändere auf:

    std::vector<double>::iterator out_##A=ans.end();
    

    (wobei ich mir nicht sicher bin, ob ans.end() wirklich auf das letzte element zeigt oder auf den bereich nach dem letzen... ans.end()-- ???)
    funktioniert es nicht - auf diesem wege komme ich wohl auf keine grünen zweig.

    danke für die antworten,

    thomas



  • Nimm std::list!



  • std::list hat den Nachteil, dass man nicht per index an die Elemente rankommt. Und bei so ner Frage-Antwort-Geschichte kann ich mir das schon vorstellen...

    Das Problem, in das du rennst, ist einfach, dass std::vector ein Array zum Halten der Daten verwendet. Wenn du da etwas anhängst, muss ein größeres Array erstellt werden, der alte Inhalt da rein kopiert, Element angehangen und altes Array zerstört werden. Die Adressen der einzelnen Werte im Array sind jetzt natürlich andere.

    Wenn du weiterhin std::vector verwenden willst, kannst du dir behelfen, indem du gleich nach dem Erstellen genügend Platz für deine ganzen Werte reservierst -> std::vector::reserve().
    Dabei bleiben Iteratoren gültig, solange ein insert() oder push_back() nicht die reservierte Größe überschreitet!



  • Ok, danke für die Tipps.
    Die Lösung mit der reservierte Größe klingt nur bedingt gut, denn wenn ich schon so arbeite, kann ich gleich mit herkömmlichen c-arrays arbeiten - in beiden Fällen habe ich Probleme, wenn ich ein Element außerhalb der reservierten Größe hinzufüge.
    ich habe das jedoch so gelöst, dass ich mit std::list arbeite, die iteratoren auf ans.begin() zeigen lasse und per std::advance(out_blubb,ans.size()-1) auf das richtige Element zeigen lasse.

    Vielen Dank nochmal für die Antworten.



  • Bei std::list werden die Iteratoren auf Elemente nicht ungueltig, wenn welche eingefuegt oder geloescht. Davon ausgenommen ist das geloeschte Element.



  • Reservist schrieb:

    Wenn du weiterhin std::vector verwenden willst, kannst du dir behelfen, indem du gleich nach dem Erstellen genügend Platz für deine ganzen Werte reservierst -> std::vector::reserve().
    Dabei bleiben Iteratoren gültig, solange ein insert() oder push_back() nicht die reservierte Größe überschreitet!

    Wobei die Lösung nicht wirklich schön ist... Wenn mal etwas mehr angehängt oder eingefügt wird, funktioniert der Kram nicht mehr, bzw. raucht ab und zeigt Müll an.

    Da würde ich auch die std::list bevorzugen, weil dort nur echt eingehängt bzw. angehängt wird. Die Objekte bleiben an der gleichen Adresse. Wenn du eine Speziallösung brauchst, ist so eine Liste auch relativ schnell selbst programmiert...


Log in to reply