Unregelmäßiges Verhalten bei Vector, Iterator und erase?



  • Hallo zusammen,

    Die Personendaten von Familienmitglieder werden in einem Formular 'Familiendetails' bearbeitet oder gelöscht. Nach Verlassen

    des Formulars wird ermittelt (siehe Code unten), welche Personendaten zurückgeschrieben (gespeichert) werden sollen und

    welche Daten gelöscht werden sollen.

    Problem: Nach dem Löschen einzelnen Personen kommt es zu 'unregelmäßigem' Programmverhalten. Beim Test mit vier Familien

    bestehend aus jeweils vier Personen wird je nach Reihenfolge der Bearbeitung mal diese, mal jene Person 'nicht' gelöscht.

    Ich vermute ein Problem in Zusammenhang mit Vector, Iterator und erase.

    int iPersonenZurueck=0;
    for(int i=0; i<gvFamSub.size(); i++){
        if(gvFamSub.at(i).iId_Fam == iId_Fam){
             // miCtr_cnt enthält Anzahl der zurückgegebenen Personen
            if(iPersonenZurueck < iPersonenGesamt){
                strcpy(gvFamSub.at(i).VN, gfldEdit_VN[iPersonenZurueck]->Text.c_str());
                strcpy(gvFamSub.at(i).NN, gfldEdit_NN[iPersonenZurueck]->Text.c_str());
            }else{
                // die übrigen gvFamSub löschen
                int iID = gvFamSub.at(i).iID; // ID der Person
                vector<TFamSub>::iterator iterFamSub;
                iterFamSub = remove_if(gvFamSub.begin(), gvFamSub.end(), CheckFamSub_v1(iID)); // Iterator zuweisen
                if(iterFamSub != gvFamSub.end())
                {
                   gvFamSub.erase(iterFamSub, gvFamSub.end() );
                }
            }
            iPersonenZurueck++;
        }
    }
    

    Erläuterungen zum Code:

    Der Vector gvFamSub vom Datentyp TFamSub (eigener Typ s.u.) enthält Personeneigenschaften, ID, ID_Fam, VN, NN

    Rückgabewerte von Formular 'Familiendetails':
    gfldEdit_VN[iPersonenZurueck]
    gfldEdit_....

    Anzahl Personen, die zur Bearbeitung an Formular 'Familiendetails' übergeben wurden:
    iPersonenGesamt

    Anzahl Personen, die nach der Bearbeitung zurückgegeben werden:
    iPersonenZurueck

    Gruß Leo



  • Hallo,

    Du hast da eine Schleife über alle Elemente von gvFamSub. Darin rufst du aber remove_if auf welches im Erfolgsfall ja deinen vector so umsortiert, dass alle zu löschenden Elemente ans Ende geschoben werden. Dadurch können Inkonsistenzen auftreten.
    Ich würde den ganzen Algorithmus überarbeiten. Über das wie kann ich jetzt zu wenig sagen, weil ich hier nicht weiß was du genau machen willst.

    Noch eine kleine Nebenfrage. Wieso verwendest du char Arrays als strings? Da gibt es doch schön AnsiString oder std::string.



  • Danke Braunstein für die Antwort.

    Shit, beim Vereinfachen des Codes zum Posten habe ich zwei Fehler reingebaut.
    Jetzt ist's überarbeitet.

    zu 1)
    Der besagte Code wird abgearbeitet, wenn in Formular 'Familiendetails' Personen gelöscht wurden. Siehe if-Abfrage unten: also wenn weniger Personen zurückgegeben werden als hingegeben.

    In iPersonen werden die Personen, die zur Familie iID_Fam gehören hochgezählt. Diejenigen, die zurückgeschrieben werden, übernehmen die Werte aus gfldEdit_..., die übrigen werden gelöscht.

    if(iPersonenZurueck == iPersonenHin){
      // (...)
    }else if(iPersonenZurueck > iPersonenHin){
      // (...)
    }else{ // wenn weniger Personen zurück als hin
    
      int iPersonen=0;
      for(int i=0; i<gvFamSub.size(); i++){
        if(gvFamSub.at(i).iId_Fam == iId_Fam){
           // miCtr_cnt enthält Anzahl der zurückgegebenen Personen
          if(iPersonen < iPersonenZurueck){
            strcpy(gvFamSub.at(i).VN, gfldEdit_VN[iPersonen]->Text.c_str());
            strcpy(gvFamSub.at(i).NN, gfldEdit_NN[iPersonen]->Text.c_str());
          }else{
            // die übrigen gvFamSub löschen
            int iID = gvFamSub.at(i).iID; // ID der Person
            vector<TFamSub>::iterator iterFamSub;
            iterFamSub = remove_if(gvFamSub.begin(), gvFamSub.end(), CheckFamSub_v1(iID)); // Iterator zuweisen
            if(iterFamSub != gvFamSub.end()){
               gvFamSub.erase(iterFamSub, gvFamSub.end() );
            }
          }
          iPersonen++;
        }
      }
    }
    

    zu 2)
    Bevorzugt nutze ich AnsiString, aber immer wieder gab es auch Situationen, da waren char-Arrays gefragt.



  • Dein erase löscht eine range und keinen einzelnen Eintrag. Kann das zu einem Problem führen?
    Ansonsten würde ich mir alle zu löschenden IDs merken und die am Ende in einem Rutsch per remove-erase löschen.



  • Dass die Anweisung einen ganzen Bereich löschen soll, wundert mich. Als Kriterium lege ich doch eine eindeutige ID fest

    CheckFamSub_v1(iID)
    

    Diese Technik hat in anderen Projekten nie Probleme bereitet.

    Kann es evtl. daran liegen, dass sich da etwas 'verschluckt', weil ich mehrfach nacheinander lösche?



  • Überleg mal was passiert.

    Du zählst von 0 is Ende
    for(int i=0; i<gvFamSub.size(); i++){

    Nehmen wir mal an die 0 wird in der Schleife gelöscht
    was passiert mit den restlichen 1 bis Ende?

    Die rutschen eins nach vorne also aus 1 wird 0, 2 wird 1 usw.

    Nächster Schleifendurchlauf geht mit 1 weiter, holla was ist nun mit der neuen 0?
    Ja... die wird einfach ignoriert oder wie du so schön schreibst 'verschluckt'.

    Um diesen Denkfehler zu verhintern ist es em einfachsten von hinten nach vorne zu durch die Schleife zu gehen

    for(int i=gvFamSub.size()-1; i>=0; --i){



  • Wozu dann überhaupt die Schleife? Zum Löschen braucht man sie hier nicht.



  • Überleg mal schrieb:

    Überleg mal was passiert.

    Du zählst von 0 is Ende
    for(int i=0; i<gvFamSub.size(); i++){

    (...)

    Um diesen Denkfehler zu verhintern ist es em einfachsten von hinten nach vorne zu durch die Schleife zu gehen

    for(int i=gvFamSub.size()-1; i>=0; --i){

    Besten Dank für die Antwort. Das leuchtet total ein. Und da sehe ich auch den Unterschied zu bisherigen Projekten. Da ist das Löschen nie aus so einer Schleife heraus passiert.

    Gruß
    Leo


Anmelden zum Antworten