C++ Klasseninstaz aus Liste löschen verursacht einen Fehler..

Abcd12345

Lt. Junior Grade
Registriert
März 2006
Beiträge
483
Hi,

Auf anraten des Autors des Buches, welches ich grade lese um C++ zu lernen, versuche ich das gelernte Wissen durch experiementieren zu festigen.

Damit das ganze nicht zu langweilig wird hab ich mir selber eine kleine Aufgabe gestellt. Ich versuche grade eine Abfrage zu erstellen die es dem Benutzer erlaubt eine Beliebiege Zahl von Instanzen der Klasse Raumschiffe zu erstellen. Danach wird ein Menue aufgerufen indem man 3 Funktionen zur Auswahl hat.

1. Ein Schiff in die Schlacht schicken wobei es zerstoert wird und aus der Liste geloescht werden soll.
2. Die liste anzeigen
3. Spiel beenden

Das Problem ist nun bei Punkt 1. Wenn ich versuche das betreffende Raumschiff mit " lRaumschiffe.erease (*i); " aus der Liste zu löschen bekomme ich einen Fehler und das Programm stuertz ab. Beim kompelieren erhalte ich allerdings keinen Fehler..

Was ist verkehrt?

Code:
#include <iostream>
#include <list>

using namespace std;

class CRaumschiff
{
private:

    //Membervariablen
    int mID;
    int mEnergie;

public:

    //Memberfunktionen
    CRaumschiff (int ID)
    {
        mID = ID;
        cout << "Raumschiff " << mID << " wurde gebaut" << endl;
    }

    ~CRaumschiff ()
    {

        cout << "Raumschiff: " << mID << " zerstört";
    }

   
    //Life abfrage..
    bool Lifeabfrage ()
    {
        if (mEnergie == 0)
        {
            return true;
        }
        else
        {
            return false;
        }

    }

        int IDRueckgabe ()
    {

            return mID;

    }

    void Kampf ()
    {
        mEnergie = 0;
        cout << "Raumschiff wurde zerstoert" << mID << endl;

    }

};

//Prototypen
void AnzeigeFunktion ();

int main ()
{
    //Variablen
    list<CRaumschiff*> lRaumschiffe;
    list<CRaumschiff*>::iterator i;
    int Anzahl;
    int Auswahl2 = 0;

    cout << "Wieviele Raumschiffe sollen gebaut werden? ";
    cin >> Anzahl;
    cout << endl;

    //Zeiger deklaieren
    CRaumschiff *TEMP = NULL;

    for(int j=0; j<Anzahl; j++)
    {

        TEMP = new CRaumschiff(j+1);

        lRaumschiffe.push_back(TEMP);

    }

    //Menue
    do {

    cout <<"\n\n1)  Raumschiffe in die Schlacht schicken: " << endl;
    cout << "2)  Raumschiffe anzeigen"<< endl;
    cout << "3)  Beenden"<< endl;

    cout << "Eingabe machen: ";
    cin >> Auswahl2;

    switch(Auswahl2)
    {
        case(1):
        {
            cout << "Raumschiff NR? ";
            cin >> Auswahl2;

            for(i = lRaumschiffe.begin(); i != lRaumschiffe.end(); i++)
            {
                if ((*i)->IDRueckgabe()== Auswahl2)
                {
                    (*i)->Kampf ();

                    lRaumschiffe.erase(i);
                }
            }
        }
    break;
       
        case(2):
        {
    cout << "Es befinden sich " << static_cast<int> (lRaumschiffe.size());
    cout << " Elemente in der Liste";
        }

        break;

        case(3):
        {
        }
        break;


        default:
        {
        }


    }

   
    } while(Auswahl2 != 3);




   
    return 0;

}


void AnzeigeFunktion (list<CRaumschiff> testliste)
{

    cout << "Es befinden sich " << static_cast<int> (testliste.size());
    cout << " Elemente in der Liste";


}
 
Ganz einfach. Das Problem liegt in der Schleife zum löschen des Items.
Nachdem du ein Item gelöscht hast, wird die Liste ja kleiner, deine Schleife will aber trotzdem alle Items durcharbeiten. Insgesamt eine unglückliche Lösung.

Mach es doch so:
Code:
iter = lRaumschiffe.begin();
for(int i=0; i < Auswahl2; i++)
            {
                iter++;
            }
            lRaumschiffe.erase(iter);
Bei mir heißt der Iterator iter, ich würde Namen wie i/j wirklich nur in Schleifenköpfen verwenden, wo der Gültigkeitsbereich eindeutig ist.

Mir fällt auf, dass du immer wieder hier mit einzelnen Problemen im Forum um Hilfe fragst. Das mag generell nicht schlimm sein, aber man sollte auch lernen, selbst Fehler zu finden, auch wenn es mal länger gehen sollte. Falls du es noch nicht gelesen hast, das Kapitel in dem Buch über den Debugger ist wirklich hilfreich. Du hättest den Fehler bestimmt in 5-10 Minuten gefunden, wenn du einmal im Einzelschrittmodus durchgegangen wärst.

Edit: Im übrigen würde nich nie eine Variable für 2 Verwendungszwecke (Menü + Schleife) benutzen. Man bereut sowas schnell.
 
Zuletzt bearbeitet:
Ich hab mir das Kapitel mit dem Debugger schon durchgelesen und ich werde versuchen das nächste mal erstmal mit seiner Hilfe das Problem zu loesen.

Ich muss wohl erstmal erkennen wie sinvoll das Teil ist ;)


Achja noch ne Frage: Der Speicher wird doch komplett freigegeben, wenn ich die Raumschiffe rausloesche oder? Ich muss also nicht noch extra delete(*i) oder sowas machen?
 
Zuletzt bearbeitet:
Nein, es werden nur die in der Liste gespeicherten Adressen gelöscht. Diese stehen ja erstmal in keiner Verbindung mit den Daten die dahinter liegen.

In diesem Falle hätte ich auch keine Pointer genommen sondern die Klasseninstanzen direkt in der Liste gespeichert. Der Speicherverbrauch ist gleich und du ersparst dir einen unübersichtlichen Code.

Edit:

Zur besseren Verständlichkeit:
Pointer sind quasi ein bestimmter Variablentyp, in den genau eine Speicheradresse passt. Man könnte sich also auch stattdessen einen Datentyp POINTER deklarieren, den man dann mit "POINTER pVariablenname;" benutzt und in den man dann Adressen wie 0x001234 speichert.
Der Datentyp vor einem Pointer ist nur dafür da, um dem Programm zu sagen, wie weit der Pointer auf dem Speicher lesen soll, zum Beispiel ein char mit 1 Byte oder deine Klasseninstanz mit vielleicht 100Byte.
Vielleicht hat das dir geholfen.
 
Zuletzt bearbeitet:
Wenn ich jetzt die Instanzen direkt in die Liste einfüge und nicht ihre zeiger, werden sie dann gelöscht wenn ich die listen erase Funktion benutze?

In dem Buch, was du auch gelesen hast werden nur Zeiger von Instanzen in Listen gespeichert, was hat das denn für einen Sinn? Damit sie auch ausserhalb der Main FUnktion benutzt werden können?

Pointer = Zeiger?
 
Zuletzt bearbeitet:
Pointer = Zeiger.

Wenn du die erase() Funktion benutzt, wird einfach das Item, was vorher auch in der Liste erstellt wurde, ob Zeiger, Integer oder Klasseninstanz, gelöscht.

Eine mögliche Anwedungsform wäre wie du sagst, dass man new/delete benutzen kann und dessen Vorteile nutzen kann, man aber wie du siehst auch seine Nachteile in kauf nehmen muss.

Tut mir leid für den kurzen Text, hab grad etwas Migräne.
 
Joar, kein Problem mit Migräne ist nicht zu spaßen ;)

Ich hoffe das ich es jetzt richtig verstanden habe :). Also wenn ich einen Zeiger in der Liste einspeicher und diesen so lösche:

Code:
listBelibige.erase(i)

dann wird sozusagen nur die Adresse der Instanz gelöscht, nicht jedoch die Instanz selber. Wenn ich so vorgehen würde könnte ich auch später nicht mehr auf den HeapSpeicher zugreifen der mit der Instanz gefüllt ist, da ich die einzige Adresse gelöscht habe :( Ein schönes Memoryleak also..

Code:
delete (*i)
listBelibige.erase(i)

So wäre es dann besser, da ich hier jetzt erst die Instanz lösche und dann den Zeiger auf die Instanz.

Ich hoffe mal das ich es so weit richtig verstanden habe :)


Wenn ich jetzt eine Klassen Instanz direkt in der Liste speicher so z.b:

Code:
list<CRaumschiff> listRaumschiff;
CRaumschiff MeinRaumschiff;
listRaumschiff.push_back (Mein Raumschiff);

Und jetzt so wieder aus der Liste entferne:

Code:
listRaumschiff.erase(i)

Lösche ich dann diesmal die Instanz oder auch wieder nur den Listeneintrag und die Instanz exestiert nach dem löschen noch, nur nicht in der Liste?
 
Das Programm hat einen weiteren Bug. Wenn du mit erase einen Iterator löscht, wird dieser ungültig und du darfst ihn nicht mehr verwenden. Das schließt inkrementieren ein. Deshalb gibt erase einen neuen Iterator zurück:

i = list.erase(i);

In dem Fall brauchst und darfst du den Iterator nicht mehr inkrementieren, da er automatisch der nächste Iterator ist.
 
@7H3 N4C3R:

Das würde aber keine Rolle spielen, wenn er wieder von vorne anfängt und den Iterator auf List.begin() setzt.

@Abcd1234:

Das ist alles soweit richtig wie du es ausgeführt hast. Im letzten Beispiel löscht du NUR die Instanz in der Liste. Deine selbst erstellte Instanz wird erst dann gelöscht, wenn das Programm aus dem Gültigkeitsbereich "{}" der Instanz läuft.
 
Spartaner117 schrieb:
Das würde aber keine Rolle spielen, wenn er wieder von vorne anfängt und den Iterator auf List.begin() setzt.
Naja man kann sich auch die Hose mit ner Kneifzange anziehen ;) *scnr*

Funktioniert zwar, macht aber absolut keinen Sinn, da du so den Sinn der Liste und der Iteratoren ad absurdum führst.
 
Zurück
Oben