Alternative zu "unsigned char AArray[x]" gesucht



  • Okay,

    Danke erstmal für die Antwort! Nachdem ich nun auto_ptr verwende habe ich auch keine Angst mehr vor der STL. Ich habe mir jetzt auf dieser Seite mal std::vector angeschaut und wollte jetzt iterator erase(iterator Pos) verwenden, was bei mir so aussieht:

    vArray.erase(Var);
    

    , wobei Var eine lokale Integer-Variable ist, in der steht welche Position gelöscht werden soll.
    vArray steht im Header so:

    std::vector<unsigned char> vArray;
    

    Wenn ich das Ganze compilieren will, bekomme ich folgende Meldung:

    [C++Fehler] DispCfg.cpp(137): Could not find a match for 'std::vector<unsigned char,std::allocator<unsigned char> >::erase(int)'.

    Also ist der übergebene Parameter falsch, oder? Wie soll ich sonst den Index des zu löschenden Elemnts übergeben? 😕



  • Hallo

    Nein erase erwartet nicht einen Index, sondern einen Iterator. Das ist bei std::vector zwar etwas umständlich, aber da die ganzen Container ja ein ähnliches Interface haben sollen so von den Entwicklern designed.

    Du kannst den Index aber in einen Iterator umrechnen :

    std::vector<unsigned char> vArray;
    int index = ...;
    std::vector<unsigned char>::iterator to_delete = vArray.begin();
    std::advance(to_delete, index);
    vArray.erase(to_delete);
    

    bis bald
    akari



  • Also jetzt mal ganz ehrlich: Ich hatte gehofft durch Verwendung eines Containers den Quellcode vereinfachen zu können, aber das hier schlägt ja dem Fass den Boden aus!
    Das sind ja 4 Schritte bis zum Entfernen des Elements! Da fand ich die for-Schleife wesentlich einfacher (und viell. sogar effektiver?) - mit der habe ich einfach ab der Position <Index> den nächsten Wert dem davor zugewiesen bis ich mit dem Array durch war - die hatte nur 2 Zeilen und man hat auf einen Blick gesehen, was da gemacht wird!
    Wenn das mit dem Vertauschen zweier Elemente ähnlich kompliziert ist, packe ich das std::vector-Ding sofort wieder in die Schublade!



  • Naja, das entsteht aber weil Du zwei Zugriffstechniken für den Vektor verwendest. Die STL arbeitet in den Containern mit Iteratoren um kompatibel mit Container zu sein, die keinen wahlfreien Zugriff über Index besitzen. Wenn Du rein mit Iteratoren und ohne Indicies arbeitest sieht das schon wieder ganz anders aus.



  • witte schrieb:

    ... Wenn Du rein mit Iteratoren und ohne Indicies arbeitest sieht das schon wieder ganz anders aus.

    Das sagt sich so schön... Allerdings habe ich keine Idee, wie ich das machen soll. Ich habe eine ListBox und wenn ein Eintrag aus dieser entfernt wird, soll das zugehörige Element des Array auch entfernt werden. Im Array stehen Nummern die identifizieren, welchen Inhalt der ListBox-Eintrag hat.

    Bsp: ListBox hat an Position 3 (ItemIndex= 2) einen Eintrag der entfernt wird.
    Im Array steht in Element 3, welchen Inhalt der Eintrag hat. Da da Eintrag entfernt wird, muss das Element auch entfernt werden - das Array spiegelt sozusagen den Inhalt der ListBox wieder.



  • Ein Vektor erlaubt wahlfreien Zugriff und benötigt daher einen Random Access Iterator. Dieser sollte Iteratorarithmetik unterstützen. Wenn Du also unbedingt einen Einzeiler willst dann mach

    vArray.erase(vArray.begin() + 2);



  • Super, Dankeschön! 🙂 Und was ist daran jetzt schlecht? Warum schlägt akari so eine umständliche Lösung vor? Einfach weil er die Rahmenbedingungen nicht kannte, oder hat sein Vorschlag noch andere Vorteile?

    Ich lösche dann also ein Element jetzt so:

    vArray.erase(vArray.begin() + Var);
    


  • Sein Vorschlag funktioniert für alle Containertypen. Wenn Du beispielsweise ständig die Elemente vertauschst, einfügst und löschst könnte eine std::list besser geeignet sein als ein Vektor mit dem Nachteil, dass sie keinen wahlfreien Zugriff ermöglicht. Sie verwendet einen bidirektionalen Iterator, mein Vorschlag wäre dann unbrauchbar.



  • std::swap(vArray[Var], vArray[Var-1]);
    

    funktioniert auch... Jetzt werd' ich das mal laufen lassen und testen, ob die Werte in vArray stimmen.

    witte schrieb:

    Sein (akari's) Vorschlag funktioniert für alle Containertypen. Wenn Du beispielsweise ständig die Elemente vertauschst, einfügst und löschst könnte eine std::list besser geeignet sein als ein Vektor mit dem Nachteil, dass sie keinen wahlfreien Zugriff ermöglicht. Sie verwendet einen bidirektionalen Iterator, mein Vorschlag wäre dann unbrauchbar.

    Alles, was ich machen wollte funktioniert, aber andererseits vertausche und lösche ich auch Elemente - ich füge allerdings keine Elemente ein, sondern hänge nur an. Wie definierst du "ständig"? Es wird jetzt nicht 1000x in der Minute etwas getauscht oder gelöscht - das passiert 1x pro entsprechender Benutzereingabe (mausklick auf Button). Ist dann vector Ok dafür oder sollte ich trotzdem besser list nehmen?



  • Nimm vector, bei zehn Listboxeinträgen sollte es trivial sein. Wenn Du dann irgendwann "Herr-der-Ringe-Triologie-Videos" in einem Vektor speichern willst und diese ständig umherkopieren willst reden wir nochmal drüber.



  • Juhu! Mein erstes Mal "std::vector" funktioniert 😃

    Vielen Dank akari und witte 👍

    PS: Das mit den Iteratoren hab' ich allerdings noch nicht ganz verstanden - was ist das für eine merkwürdige Lebensform?



  • Hallo

    Iteratoren sind ein weiteres Merkmal der schon genannten Austauschbarkeit von verschiedenen Containertypen. Wenn du zum Beispiel std::vector und std::list vergleichst, wirst du feststellen das std::list konzeptbedingt keinen []-Operator bzw. generell keinen wahlfreien Zugriff bietet.
    Das führt stätestens dann zu Problemen wenn du mal einen Containertyp von vector zu list austauschen willst. Dann must du schlimmstenfalls große Teile deines index-basierenden Programms umschreiben.
    Mit iteratoren hingegen können auch völlig unterschiedliche Containertypen gleich behandelt werden. Insbesondere erlauben diese auch sehr umfangreiche Metaprogrammierungen mit Templates.
    Dieses Thema ist aber wohl zu umfangreich, um in ein paar Sätzen abgehandelt zu werden. Arbeite erstmal weiter mit std::vector, du wirst schon irgendwann an den Punkt kommen wo du die nützlichkeit von Iteratoren verstehst. Nicht zu vergessen das dieses allgemeine, nicht Builder-spezifsche Thema schon in C++ Forum und auch in den Magazinartikeln behandelt wurde.

    bis bald
    akari



  • akari schrieb:

    Iteratoren sind ein weiteres Merkmal der schon genannten Austauschbarkeit von verschiedenen Containertypen. Wenn du zum Beispiel std::vector und std::list vergleichst, wirst du feststellen das std::list konzeptbedingt keinen []-Operator bzw. generell keinen wahlfreien Zugriff bietet.
    Das führt stätestens dann zu Problemen wenn du mal einen Containertyp von vector zu list austauschen willst. Dann must du schlimmstenfalls große Teile deines index-basierenden Programms umschreiben.
    Mit iteratoren hingegen können auch völlig unterschiedliche Containertypen gleich behandelt werden.

    Ok, soweit war mir das schon klar und so steht es ja überall geschrieben.

    akari schrieb:

    Arbeite erstmal weiter mit std::vector, du wirst schon irgendwann an den Punkt kommen wo du die nützlichkeit von Iteratoren verstehst.

    Das ist ja das Problem: Das ich bei Nutzung der STL unbedingt wissen muss WAS Iteratoren sind - um ihre Nützlichkeit weiß ich im Prinzip schon, auch wenn es nur aus der Überlegung heraus ist, dass ich sie brauche um die STL sinnvoll zu nutzen. Du hast mir (und dafür natürlich Danke) jetzt nochmal grob erklärt was die Vorteile und der Sinn von Iteratoren sind. Die für mich wichtige Frage ist aber: Was sind Iteratoren genau? Aber ich werd' mich dazu auf jeden Fall die Tage belesen!

    witte schrieb:

    ... Wenn Du also unbedingt einen Einzeiler willst ...

    Dazu nochmal: Das liest sich, als wäre es nicht erstrebenswert den Quellcode kurz zu halten?! Ich halte das eigentlich so, dass ich Quellcode so kurz wie möglich aber so lang wie nötig schreibe - Somit passt mehr Funktionalität rein, ohne dass es unübersichtlich wird. Ich bin noch lange nicht soweit mir Gedanken über Wiederverwendung von Programmteilen zu machen, dazu sind die Programme die ich zu schreiben habe viel zu klein und von zu geringer Anzahl. Auch gibt es hier niemanden, der meine Programmteile verwenden möchte, da die Kollegen die hier noch (teilweise nur am Rande) programmieren komplett auf C-Style sind und somit mit meinen Programmen schon garnichts mehr anfangen könnten (vom Verständnis her). Vor diesem Hintergrund sollte das doch Ok sein, oder?



  • Kolumbus schrieb:

    akari schrieb:

    Arbeite erstmal weiter mit std::vector, du wirst schon irgendwann an den Punkt kommen wo du die nützlichkeit von Iteratoren verstehst.

    Das ist ja das Problem: Das ich bei Nutzung der STL unbedingt wissen muss WAS Iteratoren sind - um ihre Nützlichkeit weiß ich im Prinzip schon, auch wenn es nur aus der Überlegung heraus ist, dass ich sie brauche um die STL sinnvoll zu nutzen. Du hast mir (und dafür natürlich Danke) jetzt nochmal grob erklärt was die Vorteile und der Sinn von Iteratoren sind. Die für mich wichtige Frage ist aber: Was sind Iteratoren genau? Aber ich werd' mich dazu auf jeden Fall die Tage belesen!

    Das ist ja das schöne an den STL-Containern : Du kannst grade std::vector so benutzen als würdest du mit einem herkömmlichen dynamischen C-Array arbeiten, nur sicherer. Wenn du also erstmal weiter mit Indexen arbeitest machst du nichts falsch. Du must nicht mit Iteratoren arbeiten (hm... nagut wenn du erase anwenden willst dann doch).
    Iteratoren sind transparente Hilfskonstrukte der Container, d.h. ohne Container keine Iteratoren. Am Anfang hilft es wenn du dir einen Iterator als Zeiger vorstellst. Denn wenn du mit C-Arrays arbeitest wirst du vielleicht schon die Pointer-Arithmetik kennen gelernt haben :

    int Array[10]; // Einfaches Array
    int* Pointer = &Array; // Pointer auf das erste Element des Arrays
    int* End = Pointer +10; // Pointer auf des Speicherbereich direkt nach dem letzten Element des Arrays. 
    // Dieser Pointer darf nicht dereferenziert werden, wir brauchen nur seine Adresse als Schluß-Markierung 
    for (; Pointer != End; Pointer++) // Iteration über alle Elemente
    {
      cout << *Pointer << endl; // Ausgabe des Elementes
    }
    

    Iteratoren sind nun Klassen die das Verhalten von Pointern soweit nachbauen. D.h. die Arithmetik-Operatoren (+,-,++,--) und der Dereferenzierungsoperator (*, ->) wurden überladen. Und deshalb sehen Iteration auch sehr ähnlich aus :

    std::vector<int> Array; // Einfaches Array
    std::vector<int>::iterator It = Array.begin(); // Iterator auf das erste Element des Arrays
    std::vector<int>::iterator End = Array.end(); // Iterator auf des Speicherbereich direkt nach dem letzten Element des Arrays. 
    // Dieser Iterator darf nicht dereferenziert werden, wir brauchen nur seine Adresse als Schluß-Markierung 
    for (; It != End; It++) // Iteration über alle Elemente
    {
      cout << *It << endl; // Ausgabe des Elementes
    }
    

    Der Vorteil des Iterators gegenüber dem rohen Pointers ist das sich das Iterator-Konzept auch auf Container anwenden läßt bei dem rohe Pointer-Arithmetik nicht funktioniert (Eben alles außer std::vector).

    witte schrieb:

    ... Wenn Du also unbedingt einen Einzeiler willst ...

    Dazu nochmal: Das liest sich, als wäre es nicht erstrebenswert den Quellcode kurz zu halten?! Ich halte das eigentlich so, dass ich Quellcode so kurz wie möglich aber so lang wie nötig schreibe - Somit passt mehr Funktionalität rein, ohne dass es unübersichtlich wird. Ich bin noch lange nicht soweit mir Gedanken über Wiederverwendung von Programmteilen zu machen, dazu sind die Programme die ich zu schreiben habe viel zu klein und von zu geringer Anzahl. Auch gibt es hier niemanden, der meine Programmteile verwenden möchte, da die Kollegen die hier noch (teilweise nur am Rande) programmieren komplett auf C-Style sind und somit mit meinen Programmen schon garnichts mehr anfangen könnten (vom Verständnis her). Vor diesem Hintergrund sollte das doch Ok sein, oder?

    Selbstverständlich ist es gut kompakten lesbaren Code zu schreiben. In diesem speziellen Fall ist wittes Lösung sicher angebracht. Solange du immer nur ein Element aufeinmal löschen willst läßt sich da nichts mehr optimieren.
    Aber vielleicht hast du ja auch irgendwann mal die Aufgabe eine ganze Reihe von Elementen aufeinmal zu löschen. Wenn du dann für jedes Löschen jeweils einen neuen Iterator aufstellst und weiterschiebst, hast du keinen Vorteil gegenüber dem rohen Array.
    Wenn du aber gleich alle Elemente mit einen Iterator durchgehst und bei zutreffenden Elementen erase anwendest dann bekommst du nämlich sogar noch einen minimalen Geschwindigkeitsvorteil gegenüber einem rohen Array.

    bis bald
    akari



  • Es gibt noch die find() und remove_if Funktionen, mit denen das schon etwas handlicher aussieht:

    std::vector<unsigned int> v;
    
    // alle 5 entfernen
    v.erase( std::remove_if( v.begin(), v.end(), 5 ), v.end() );
    
    // die erste 4 entfernen
    v.erase( std::find( v.begin(), v.end(), 4 ) );
    


  • Vielen Vielen Dank für die ausführlichen Erklärungen akari und Danke für die Ergänzung _DocShoe_! Ich werde ab jetzt versuchen an passenden Stellen immer mit std::vector zu arbeiten. 👍

    MfG


Anmelden zum Antworten