STL-Iteratoren casten



  • Hi!

    Ich arbeite grad viel mit den STL-Iteratoren und hab mal eine Frage:
    Angenommen, ich habe einen string::iterator, der "zeigt" ja quasi auf ein Element (char) im String. Ich meine auch mal gelesen zu haben, dass in den "normalen" Containern wie string und vector alle Elemente hintereinander abgelegt werden - soweit der Compiler std-conform ist.
    Deshalb ist es ja auch möglich, jenen String-Iterator in einen char* zu casten. Ist aber nicht besonders schön:

    string::iterator s;
    char* str2 = (char*) &(*(is));
    

    Gibts da eine andere Möglichkeit, oder sollte man das grundsätzlich vermeiden? Ich hab irgendwie das Gefühl, dass ich das an einigen Stellen brauche, von daher wärs schön 😋

    Natürlich auch analog vector<xy>::iterator 🙂


  • Mod

    Badestrand schrieb:

    Hi!

    Ich arbeite grad viel mit den STL-Iteratoren und hab mal eine Frage:
    Angenommen, ich habe einen string::iterator, der "zeigt" ja quasi auf ein Element (char) im String. Ich meine auch mal gelesen zu haben, dass in den "normalen" Containern wie string und vector alle Elemente hintereinander abgelegt werden - soweit der Compiler std-conform ist.

    vector: ja; string: nein (auch wenn es wohl keine bestehende Implementation kaputtmachte, wenn man das auch für string festlegte).

    Deshalb ist es ja auch möglich, jenen String-Iterator in einen char* zu casten. Ist aber nicht besonders schön:

    string::iterator s;
    char* str2 = (char*) &(*(is));
    



  • Na gut, schade 😞 Aber danke für die Antwort! Muss ich mir wahrscheinlich die Zeiger (zumindest teilweise) abgewöhnen...

    Wie macht ihr das eigentlich? Wenn man mit der STL arbeitet, müsste man ja viele Zeiger weglassen und immer mit iteratoren arbeiten:

    class SomeClass {};
    
    void DoSomething( vector<SomeClass>::const_iterator elem ) {}
    // statt
    void DoSomething( const SomeClass* elem ) {}
    
    ...
    
    vector<SomeClass> vec;
    for ( vector<SomeClass>::iterator iter=vec.begin(); iter!=vec.end(); iter++ )
        DoSomething( iter );
    

    Aber wenn ich nun nur eine Instanz von "SomeClass" haben will, dann kann ich die Funktion "DoSomething" ja gar nicht nutzen:

    SomeClass* sc = new SomeClass();
    DoSomething( sc );
    

    Wie kann man sowas lösen?



  • Badestrand schrieb:

    SomeClass* sc = new SomeClass();
    DoSomething( sc );
    

    Wie kann man sowas lösen?

    class SomeClass {};
    
    void DoSomething( const SomeClass* elem ) {}
    
    ...
    
    vector<SomeClass> vec;
    for ( vector<SomeClass>::iterator iter=vec.begin(); iter!=vec.end(); ++iter ) // ++iter
        DoSomething( *iter );
    


  • Ne, eben nicht 😕
    Wenn man den Iterator dereferenziert, kommt ein vector<..>::value_type bei raus.

    Und ob iter++ oder ++iter ist in der for-Schleife egal :p



  • überladen?



  • Checker&Murckser schrieb:

    überladen?

    Ja gut, das geht schon, aber dann hätte ich den Code doppelt 😞 Ist zwar eine Lösung, aber designtechnisch auch nur halb gebacken..

    Oh, Juchu, ich habs!! Hab gemerkt, dass push_back auch einen dereferenzierten Iterator als "const Typ&" annimmt 👍

    Also:

    void DoSomething( const SomeClass& elem ) {}
    
    vector<SomeClass> vec;
    for ( vector<SomeClass>::iterator iter=vec.begin(); iter!=vec.end(); iter++ )
    	DoSomething( *iter );
    
    SomeClass cl;
    DoSomething( cl );
    

    Freude 😃 😃 👍



  • ++iter
    

    Bei Iteratoren ++iter ! Merk dir das... 😃



  • camper schrieb:

    vector: ja; string: nein (auch wenn es wohl keine bestehende Implementation kaputtmachte, wenn man das auch für string festlegte).

    das wurde doch 2003 so festgelegt, oder?



  • Der Grund:
    ==iter inkrementiert erst und liefert dann den erhaltenen Wert zurueck, iter++ speichert den Wert, inkrementiert dann und liefert am ende den gespeicherten Wert - sprich es muss eine Kopie angelegt werden, was sowohl speicher als auch Zeit kostet. (Gilt im Endeffekt fuer alle ueberladenen inkrement-operatoren, nur bei Standardtypen wie int ist der compiler (meist) schlau genug, keinen overhead zu produzieren.



  • Hm, war mir neu, leuchtet aber ein 😃
    Aber seid ihr euch sicher, dass er den Wert speichert und zurückgibt, obwohl er gar nicht gebraucht/abgefragt wird?



  • In dem Moment, wo der op++ aufgerufen wird, ist noch gar nicht klar, ob der Rückgabewert weiterverwendet werden soll - also bleibt dem Operator gar nichts anderes übrig als ihn zu kopieren (die eigene Position wird als Nebeneffekt geändert, also stimmt sie nicht mehr mit dem überein, was der Aufrufer erwartet). Wenn du den Rückgabewert nicht nutzt, darf (⚠ nicht 'muss') der Compiler die ganze Kopiererei natürlich wegoptimieren - aber verlassen würde ich mich nicht darauf.

    PS: Und der typische Aufbau der Inkrement-Operatoren sieht so aus:

    iter& iter::operator++()
    {
      //bewege *this vorwärts
      return *this;
    }
    iter iter::operator++(int)
    {
      iter tmp=*this;
      ++*this;
      return tmp;
    }
    

  • Mod

    otze schrieb:

    camper schrieb:

    vector: ja; string: nein (auch wenn es wohl keine bestehende Implementation kaputtmachte, wenn man das auch für string festlegte).

    das wurde doch 2003 so festgelegt, oder?

    Für vector steht es explicit da 23.2.4/1

    The elements of a vector are stored contiguously, meaning that if v is a vector<T, Allocator> where T is some type other than bool, then it obeys the identity &v[n] == &v[0] + n for all 0 <= n < v.size().

    Für basic_string existiert keine solche Passage an entsprechender Stelle.


Anmelden zum Antworten