std::vector - resize() und reserve()



  • Hallo,

    ich frage mich wo genau der Einsatzzweck von reserve() liegt?

    Der Unterschied zwischen reserve() und resize() ist ja folgender:

    The capacity of a vector<> can be resized by calling either reserve() or resize(). These member functions differ in two respects. Unlike resize(), which allocates memory and initializes it with a default value, reserve() only allocates raw memory without initialization. In addition, reserve() does not change the size of a vector; it only changes the vector's capacity.

    Was genau kann man sich unter "raw memory" vorstellen und wann genau setzt man reserve() ein? 😕


  • Mod

    Wenn du im Voraus schon weißt, wieviele Elemente ungefähr (oder genau) kommen, du aber noch nicht weißt, wie diese genau aussehen, dann benutzt du reserve. Denn resize erzeugt Elemente und initialisiert diese. Das kostet Zeit und wenn man diese Elemente später sowieso überschreibt, dann kann man sich diese Zeit sparen.

    Ich persönlich benutze resize ziemlich selten, weil das Erzeugen von Vectorelementen etwas ist, was sehr selten in zeitkritischen Schleifen steht. Und bei zeitunkritischen Sachen ist es 1. egal, da unkritisch und 2. ist ein normales push_back ohne reserve auch ziemlich schnell, da die vectoren meistens eine recht clevere Strategie der Speicherreservierung haben.

    edit: Und unter raw memory kannst du dir einfach uninitialisierten Speicher vorstellen. Zum Beispiel das, was du von Funktionen wie malloc oder get_temporary_buffer zuruck bekommst.



  • Verstehe. Danke! 🙂 👍



  • Beispiel, wo reserve sinnvoll sein (kann): Wenn du nacheinander 50mal push_back aufrufst, wird der vector der Standardbibliothek im MSVC insgesamt elfmal (gcc: sieben mal) neuen Speicher besorgen. Dabei wird jedesmal der komplette Inhalt durch die Gegend kopiert, neuer Speicher angefordert und alter freigegeben...

    In einem unserer Module hier auf Arbeit gibts eine Funktion, die neben einigen komplexeren Dingen einem Vector ein neues Element hinten anfügt. Diese Funktion wird von einer anderen Funktion oft aufgerufen, mit wechselnden Argumenten, wobei vorher die Zahl der Aufrufe immer bekannt ist. Durch Einsatz von reserve hab ich der aufrufenden Funktion 7% Laufzeit sparen können.



  • hi ihr,

    hier ist ein Beispiel bei dem resize sinnvoll ist .. hat immer funktioniert in gcc4.0

    std::vector< std::vector<myData> > mMy;

    std::for_each( mMy.begin(), mMy.end(), std::bind2nd(std::mem_fun_ref(&std::vector< myData >::resize), numMy) );

    in gcc4.2 geht das aber nicht mehr, weil resize jetzt anders funktioniert ..

    hat einer von euch vielleicht einen plan was ich machen muss, um das obige Beispiel auf gcc4.2 compilieren zu können ?

    grüsse,
    bzt



  • std::for_each(v.begin(), v.end(), [n](std::vector<int> &i) { i.resize(n); });
    

    getestet mit GCC 4.7



  • bzt schrieb:

    std::vector< std::vector<myData> > mMy;

    std::for_each( mMy.begin(), mMy.end(), std::bind2nd(std::mem_fun_ref(&std::vector< myData >::resize), numMy) );

    in gcc4.2 geht das aber nicht mehr, weil resize jetzt anders funktioniert ..

    Was ist denn das Problem? Hat GCC 4.2 ne andere Signatur für vector::resize (vielleicht mit zusätzlichen Default-Parametern oder sowas)?

    hat einer von euch vielleicht einen plan was ich machen muss, um das obige Beispiel auf gcc4.2 compilieren zu können ?

    Entweder so wie rüdiger es vorgeschlagen hat oder eigene Wrapper-Funktion schreiben.

    ps: oder BOOST_FOREACH verwenden oder den Loop "klassisch" mit Iteratoren/Index hinschreiben.



  • hustbaer schrieb:

    bzt schrieb:

    std::vector< std::vector<myData> > mMy;

    std::for_each( mMy.begin(), mMy.end(), std::bind2nd(std::mem_fun_ref(&std::vector< myData >::resize), numMy) );

    in gcc4.2 geht das aber nicht mehr, weil resize jetzt anders funktioniert ..

    Was ist denn das Problem? Hat GCC 4.2 ne andere Signatur für vector::resize (vielleicht mit zusätzlichen Default-Parametern oder sowas)?

    Es war noch nie garantiert, dass man Funktionszeiger auf Std-Container-Methoden benutzen kann.



  • ich verstehe Rüdigers Antwort nicht wirklich, denn seinen Vorschlag kann ich gar nicht compilieren .. bei mir fehlen da lauter primary expressions .. vielleicht kann Rüdiger das genauer erklären .. und bei gcc40 war resize überladen und mem_fun_ref hatte damit keine probleme .. und das wurde von den Gnu Jungs getreu dem standard geändert in gcc42 ..



  • bzt schrieb:

    ich verstehe Rüdigers Antwort nicht wirklich, denn seinen Vorschlag kann ich gar nicht compilieren ..

    Dazu brauchst du einen aktuellen GCC, der C++11 Lambdas unterstützt.



  • Bei g++ 4.7 muss noch -std=c++0x als Option mitgegeben werden.



  • ist leider nicht wirklich eine option gcc upzudaten, es wäre gut eine Lösung zu finden die auf gcc42 funktioniert ..



  • Dann schreibe einen Funktor!



  • TyRoXx schrieb:

    hustbaer schrieb:

    bzt schrieb:

    std::vector< std::vector<myData> > mMy;

    std::for_each( mMy.begin(), mMy.end(), std::bind2nd(std::mem_fun_ref(&std::vector< myData >::resize), numMy) );

    in gcc4.2 geht das aber nicht mehr, weil resize jetzt anders funktioniert ..

    Was ist denn das Problem? Hat GCC 4.2 ne andere Signatur für vector::resize (vielleicht mit zusätzlichen Default-Parametern oder sowas)?

    Es war noch nie garantiert, dass man Funktionszeiger auf Std-Container-Methoden benutzen kann.

    Das hab ich ja auch nicht behauptet (ganz absichtlich nicht, da ich nicht sicher bin/war ob die exakte Signatur der Memberfunktionen vorgeschrieben ist).

    Und: meinst du es ist nicht garantiert weil die exakte Signatur nicht vorgeschrieben ist? Oder weil es irgendwie erlaubt ist Dinge wie std::vector mit "Compiler-Magick" zu implementieren, so dass man überhaupt gar keine Memberfunktionszeiger verwenden könnte, nichtmal wenn man die exakte Signatur der Funktion kennt bzw. den Memberfunktionszeiger-Typ über ein Template herleitet?
    (Das würde mich nämlich einigermassen wundern, wäre aber nicht das erste mal dass mich der Standard verwundert *g*)

    Ich hab' allerdings hauptsächlich aus Interesse gefragt, denn anscheinend ist es mit GCC 4.0 ja noch gegangen, und mit 4.2 nimmer. Muss sich also was geändert haben, mich würde interessieren was (einfach nur so).



  • das kann ich dir genau sagen, weil ich die Gnu Jungs kontaktiert habe ..

    I am fairly sure the problem is that a single resize member function
    with this signature:

    void resize(size_type, const T& = T());

    was replaced with a pair of overloaded functions with these signatures:

    void resize(size_type);
    void resize(size_type, const T&);

    This change is allowed by the standard, but means that
    &vector<myData>::resize is no longer unambiguous.

    You can disambiguate the expression by providing a target type to
    convert to, so the compiler knows which overload you mean:

    typedef void (*resize_type)(std::vector< myData >::size_type);

    std::bind2nd(std::mem_fun_ref((resize_type)&std::vector< myData

    ::resize), numMyData) );

    aber so richtig hilfreich ist das noch nicht, da ich nicht weiss wie ich dem compiler sage welchen overload er nutzen soll .. es ist wohl der zweite, aber ich verstehe auch nicht was const T& = T() bedeutet und ob es das gleiche ist wie const T& ?



  • hustbaer schrieb:

    Und: meinst du es ist nicht garantiert weil die exakte Signatur nicht vorgeschrieben ist? Oder weil es irgendwie erlaubt ist Dinge wie std::vector mit "Compiler-Magick" zu implementieren, so dass man überhaupt gar keine Memberfunktionszeiger verwenden könnte, nichtmal wenn man die exakte Signatur der Funktion kennt bzw. den Memberfunktionszeiger-Typ über ein Template herleitet?
    (Das würde mich nämlich einigermassen wundern, wäre aber nicht das erste mal dass mich der Standard verwundert *g*)

    Ich habe den Standard nicht gelesen, aber in vermute, dass zumindest die Implementation des Standardarguments von resize implementationsabhängig ist.

    void resize(size_type);
    void resize(size_type, T);
    
    //ebenfalls legal:
    void resize(size_type, T = T());
    

    hustbaer schrieb:

    Ich hab' allerdings hauptsächlich aus Interesse gefragt, denn anscheinend ist es mit GCC 4.0 ja noch gegangen, und mit 4.2 nimmer. Muss sich also was geändert haben, mich würde interessieren was (einfach nur so).

    GCC hat vielleicht einfach von der zweiten auf die erste Variante gewechselt.

    Das sind alles nur Spekulationen von mir. Ob das wirklich stimmt, ist aber unerheblich, weil man sich nicht auf die Signaturen von Standardmethoden verlassen sollte. push_back hat mit C++11 auch eine Überladung bekommen.


  • Mod

    C++11 schrieb:

    17.6.5.5 Member functions [member.functions]
    1 It is unspecified whether any member functions in the C ++ standard library are defined as inline (7.1.2).
    2 An implementation may declare additional non-virtual member function signatures within a class:
    — by adding arguments with default values to a member function signature;187 [ Note: An implementation may not add arguments with default values to virtual, global, or non-member functions. — end note ]
    — by replacing a member function signature with default values by two or more member function signatures with equivalent behavior; and
    — by adding a member function signature for a member function name.
    3 A call to a member function signature described in the C ++ standard library behaves as if the implementation declares no additional member function signatures.


Anmelden zum Antworten