Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » C / C++ (ANSI-Standard) » Korrektes Löschen von Objekten

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: > 1 < [ 2 ]
000
16.06.2010, 15:12 Uhr
~HolgerK1981
Gast



C++:
// Aufgabe der Funktion:
// Lösche Nachricht i aus dem Feld timeOutSelfMsgs[] (Typ: cMessage*)

// Es liegen nach Aufruf von Finish exakt so viele "undisposed messages" vor,
// wie der Zähler "unscheduledErasedTimeOutMsgs" hoch ist, d.h. die fehlende
// Löschung an dieser Stelle ist die Ursache für den Müll.
// Erstaunlicherweise erfolgt die Löschung eingeplanter Nachrichten problemlos,
// d.h. die erste if-Clause tut, was sie soll.

void PlcNet::eraseCMessageFromTimeOutSelfMsgs(int i)
// Die Feldnummer wird als Parameter übergeben.                                
{
    functionCalls++;                                                                  
        // Zählt, wie oft die Funktion aufgerufen wird.
    if (COMMENTS_ON) EV << "Removing TimeOutMsg " << i << " from list ..." << endl;

    if (timeOutSelfMsgs[i] != NULL)                                                  
        // Ist der Zeiger "gefüllt"?
    {
        if (COMMENTS_ON) EV << "timeOutSelfMsgs[" << i << "] is not NULL" << endl;

        // IF-CLAUSE 1
        if (timeOutSelfMsgs[i]->isScheduled())                                        
                // Ist die Nachricht eingeplant?
        {
            if (COMMENTS_ON) EV << "timeOutSelfMsgs[" << i
                                                       << "] is scheduled. (" <<  
                                                        timeOutSelfMsgs[i]->getFullName() << ")"<< endl;
            cancelAndDelete (timeOutSelfMsgs[i]);                                    
                        // Breche ab, und lösche die Nachricht.
            scheduledErasedTimeOutMsgs++;                                            
                        // Zähle die Aufrufe dieser Schleife.
        }
        
        // IF-CLAUSE 2
        else  // if (!timeOutSelfMsgs[i]->isScheduled())                              
                // Wenn Sie nicht eingeplkant ist ...
        {
            if (COMMENTS_ON) EV << "timeOutSelfMsgs[" << i << "] is NOT  
                                                      scheduled. ("
<<
                              timeOutSelfMsgs[i]->getFullName() << ")"<< endl;
            if (dynamic_cast<cMessage*>(timeOutSelfMsgs[i]) != NULL)
            {

                // Problemstelle -----------------------------------------------
                
                /*
                      Die unteren Zeilen füren jeweils zum Crash. Auch ein Trick
                      mit "Rescheduling" (erst wieder scheduleAt, und dann
                      rekursiver Aufruf von "eraseCMessageFromTimeOutSelfMsgs")
                      konnte keine Abhilfe schaffen.
                      Wie muss der Befehl an dieser Stelle lauten, damit die
                      Nachricht korrekt gelöscht wird?
                      
                      Noch einmal zur Auflistung: timeOutSelfMsgs[i] ist ein
                      Zeiger auf eine Self-Message von PlcNet, zeigt nicht auf
                      Null, "enthält" ein gültiges Objekt "cMessage", das einen
                      druckbaran Namen hat, und man kann es dennoch nicht
                      einfach löschen.
                */

                
                // delete (timeOutSelfMsgs[i]);                                      
                                // Lösche die Nachricht (Versuch A).
                
                // cancelAndDelete(timeOutSelfMsgs[i]);                              
                                // Lösche die Nachricht (Versuch B).
                
                // scheduleAt(simTime() + 0.01, timeOutSelfMsgs[i]);                  
                // scheduledErasedTimeOutMsgs--;
                // eraseCMessageFromTimeOutSelfMsgs(i);
                                // Lösche die Nachricht (Versuch C).
                
                // if (timeOutSelfMsgs[i]->isSelfMessage())                          
                                // {
                                //    delete (timeOutSelfMsgs[i]);
                                // }
                                // Lösche die Nachricht (Versuch D).
                
                // if (timeOutSelfMsgs[i]->isSelfMessage())                          
                        // {
                        //    cancelAndDelete (timeOutSelfMsgs[i]);
                        // }
                        // Lösche die Nachricht (Versuch E).
                
        // -------------------------------------------------------------
           unscheduledErasedTimeOutMsgs++;                                      
               // Zähle die Aufrufe dieser Schleife.
            }
        }
        // ---------------------------------------------------------------------
    }
    timeOutSelfMsgs[i] = NULL;                                                        
        // Setze den Zeiger auf NULL;
    if (timeOutSelfMsgs[i] == NULL)                                                  
        // Überprüfe die Löschung.
    {
        if (COMMENTS_ON) EV << "Erasing successfull." << endl;
    }
}


Dieser Post wurde am 16.06.2010 um 17:01 Uhr von ao editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
001
16.06.2010, 17:20 Uhr
ao

(Operator)


Ich verstehe die Zusammenhänge nicht ganz. Zusammengereimt: cMessage ist eine Basisklasse, und die Objekte, um die es geht, sind davon abgeleitet. Aus irgendeinem Grund sind die Objekte, die NICHT scheduled sind, so kaputt, dass sie nicht zerstört werden können. Richtig?

Denkanstöße:

Warum sind die nicht scheduled? Was ist inzwischen mit denen passiert? Sind sie vielleicht schon mal woanders zerlegt worden? Oder teilweise zerlegt worden? timeOutSelfMsgs[] enthält ja nur Pointer, was ist, wenn die auf Müll oder Teil-Müll zeigen?

Dass auf dem Objekt die Basisklassen-Methoden noch funktionieren, heißt nicht, dass das ganze Objekt noch lebensfähig ist. Insbesondere Member aus abgeleiteten Klassen können beschädigt sein, das merkst du mit dem Basisklassen-Pointer nie.

Haben die Objekte überhaupt Destruktoren? Wenn ja, dann debug doch mal da rein.

Sind irgendwelche Destruktoren nicht-virtuell, so dass ein delete auf einem cMessage-Objekt nicht durchschlägt bis in die abgeleiteten Klassen?
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
002
16.06.2010, 19:46 Uhr
~Holger1981
Gast


Hmmm ... die sind nicht "scheduled", weil Ihre Laufzeit schon vorüber ist. cMessage ist eine Basisklasse von c++, wie auch cObject und cPacket. Das erstaunliche ist ja, das die Löschung nur im "geschedulten" Fall reibungslos klappt. Ansonsten scheinen aber auch die "ungeschedulten" cMessages intakt zu sein (Überprüfung mit dynamic_cast und ->getName()).

Noch mal dazu, was das "schedulen" heißt: im OMNeT++/INET-Framework gibt es kein "wait" und kein "delay" - d.h. will man etwas timen, muss man sich selbst eine Nachricht schicken, deren Ankunftszeitraum bestimmbar ist, z.B. über:

scheduleAt(simTime()+Offset, cMessage*);

Nach der Ankunft bleibt das Objekt cMessage erhalten, ->isScheduled() liefert aber dann einfach "false". Normalerweise würde man die Nachricht einfach weiterverwenden, wenn man sich demnächst noch einmal an das Gleiche erinnern will (wieder mit scheduleAt()) - in diesem speziellen Fall werden aber die Aktivitäten von Geräten überwacht, in dem deren MAC-Adressen in timeOutSelfMsgs[i] (als String) gespeichert werden, d.h. der i-te Platz muss variabel vergebbar sein. Löscht man den alten Eintrag nicht, müllt sich das Modul selbst mit undisposed Objects zu.

... und da das Ganze in einem Modell mit mehreren 100.000 Clients (City-Scale) funktionieren soll, ist Datenmüll dummerweise nicht tolerabel.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
003
16.06.2010, 20:52 Uhr
ao

(Operator)



Zitat von ~Holger1981:
Ansonsten scheinen aber auch die "ungeschedulten" cMessages intakt zu sein (Überprüfung mit dynamic_cast und ->getName()).

Ich bin mir nicht sicher, ob das für eine gesicherte Aussage reicht. Ein dynamic_cast von cMessage* auf cMessage* bewirkt vermutlich nichts außer "return this", oder? Was willst du daran ablesen?


Zitat:
in diesem speziellen Fall werden aber die Aktivitäten von Geräten überwacht, in dem deren MAC-Adressen in timeOutSelfMsgs[i] (als String) gespeichert werden

Wie sieht das genau aus? Beim Speichern von Strings kann man bekanntlich einiges falsch machen.

Mein Verdacht geht momentan dahin, dass irgendwelche Kopierkonstruktoren oder Zuweisungsoperatoren nicht das Richtige machen, oder irgendwas anderes, was zur Folge hat, dass Objekte mehrfach zerstört werden. Gibt es vielleicht mehrere Stellen im Programm, wo dasselbe Message-Objekt über Pointer referenziert wird und wo dieses per delete zerlegt wird?

Noch mal: Kannst du in die delete-Aufrufe hineindebuggen?

Wie sehen die Ableitungen von cMessage aus, die du verwendest?

Dieser Post wurde am 16.06.2010 um 20:56 Uhr von ao editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
004
17.06.2010, 08:18 Uhr
~Holger1981
Gast


Ich gehe mal Deine Punkte durch:

1. dynamic_cast<className*>(unknownObject) liefert !NULL, wenn unknownObject vom Typ className ist. Ich hatte eigentlich gedacht, das sollte hier ausreichen.

2. cMessage hat einen char* "name", den man mit ->setName(string/const char*) beschreiben kann. Das sollte hier aber nicht das Problem sein, da es bei den geschedulten cMessages auch kein Problem ist.

3. Was Debugger angeht, so bin ich KOMPLETT unfähig. Ich benutze printf() ;-)

4. Ich verwende cMessage in "Reinform", d.h. abgeleitet wird da nichts. Also einfach über timeOutSelfMsgs[i] = new cMessage (const char*).

Ich überlege die ganze Zeit, ob ich die Löschung irgendwie umgehen kann, z.B. durch Überschreiben - da scheue ich mich aber vor, weil das eine ganze Menge Arbeit bedeuten würde ...
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
005
17.06.2010, 08:31 Uhr
ao

(Operator)



Zitat von ~Holger1981:

1. dynamic_cast<className*>(unknownObject) liefert !NULL, wenn unknownObject vom Typ className ist. Ich hatte eigentlich gedacht, das sollte hier ausreichen.


Aber das muss doch an der Stelle immer klappen. Was anderes als cMessage*-Objekte kann doch in dem Array gar nicht drinstehen, außer du hast es mit Gewalt reingecastet.

Außerdem testest du damit die RTTI, also nur Typ-Information. Die Integrität der Instanz kann trotzdem beschädigt sein.


Zitat:
2. cMessage hat einen char* "name", den man mit ->setName(string/const char*) beschreiben kann. Das sollte hier aber nicht das Problem sein, da es bei den geschedulten cMessages auch kein Problem ist.

Na ja, möglicherweise passiert aber bei den nicht-scheduled Messages irgendwas, was sie kaputt oder halbkaputt zurücklässt und was beim endgültigen delete zurückschlägt. Verpointerte Speicherbereiche gehören da zu den ersten Verdächtigen.


Zitat:

3. Was Debugger angeht, so bin ich KOMPLETT unfähig

Dann solltest du das zuerst lernen. Am besten jetzt sofort. Und dann sehen, ob von den anderen Problemen überhaupt noch was übrigbleibt.

An einem kaputten Programm Debuggen zu lernen, ist nicht gerade die ideale Ausgangsposition. Also bau dir zuerst ein Minimalprogramm, das ein paar Messages erzeugt, schedulet und wieder zerstört, ohne abzustürzen, und üb daran den Umgang mit dem Debugger. Die wichtigsten Funktionen sind Step, StepInto, Watch und Break, um die zu beherrschen brauchst du allerallerhöchstens einen halben Tag. Und wenn du das begriffen hast, dann gehste auf das eigentliche Problem los.


Zitat:
Ich überlege die ganze Zeit, ob ich die Löschung irgendwie umgehen kann ...

Ich glaube, die Löschung ist nicht das Problem, sondern nur die Stelle, wo es sich zeigt. Der Fehler passiert woanders.

Dieser Post wurde am 17.06.2010 um 08:59 Uhr von ao editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
006
17.06.2010, 09:00 Uhr
0xdeadbeef
Gott
(Operator)


Ich habe eine grauenhafte Ahnung...sieht cancelAndDelete vielleicht so aus:

C++:
void cancelAndDelete(cMessage *p) {
  delete p;
  p = NULL;
}


, wenn es eigentlich so aussehen sollte:

C++:
void cancelAndDelete(cMessage *&p) {
  delete p;
  p = NULL;
}


?
--
Einfachheit ist Voraussetzung für Zuverlässigkeit.
-- Edsger Wybe Dijkstra
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
007
17.06.2010, 10:28 Uhr
~Holger1981
Gast


So, ich habe das Problem anders gelöst. Der Trick ist tatsächlich, zur Laufzeit gar keine timeOutSelfMsgs[i] zu löschen, sondern einfach den Namen per setName() auf einen ungültigen Wert zu setzen. Da mussten noch ein paar weitere Änderungen gemacht werden, und es hat mich jetzt 1,5h gekostet, alle Stellen noch einmal durchzugehen ... aber der Sieg ist mein.

cancelAndDelete(cMessage *p) - ja, die sieht so aus - ist keine von mir geschriebene Funktion, sondern Teil von OMNeT++. Was ist denn der genaue Unterschied zu cancelAndDelete(cMessage *&p)? Referenzen scheue ich normalerweise wie der Teufel das Weihwasser.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
008
17.06.2010, 11:23 Uhr
Hans
Library Walker
(Operator)


OT, @ao: ich glaube, Du solltest bei Deiner Beitragsunterschrift noch den einbauen, sonst glaubt Dir das noch einer...

Hans
--
Man muss nicht alles wissen, aber man sollte wissen, wo es steht. Zum Beispiel hier: Nachdenkseiten oder Infoportal Globalisierung.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
009
17.06.2010, 11:32 Uhr
ao

(Operator)


Der Unterschied ist, dass bei der Version ohne Referenz zwar der Speicher freigegeben wird (delete), aber das NULL-setzen von p nur im lokalen Scope der Funktion gültig ist - außerhalb behält der Zeiger den alten Wert. D.h. man sieht ihm nicht an, dass er nicht mehr auf ein gültiges Objekt zeigt, und die Gefahr besteht, dass man ein zweites Mal delete aufruft, was in vielen Laufzeitsystemen den Heap zerstört.

Ausweg wäre, nach dem Aufruf von cancelAndDelete den Zeiger selbst auf NULL zu setzen.

Bei der Version mit Referenz wirkt die Zuweisung nicht auf eine lokale Kopie, sondern auf das von außen reingereichte Objekt.

Aber mal ne andere Frage: Wenn das die Implementierung von cancelAndDelete ist, was ist denn der Unterschied zwischen delete und cancelAndDelete? Worin besteht das Cancel?


Zitat von ~Holger1981:
... aber der Sieg ist mein.

Glaub ich nicht. Du hast den Crash nur auf später verschoben. Irgendwann musst du die Messages freigeben, spätestens beim Programmende.

Und debuggen kannst du immer noch nicht.

Sorry, aber das ist kein Sieg, bestenfalls ein vorläufiges Unentschieden.

Dieser Post wurde am 17.06.2010 um 11:37 Uhr von ao editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: > 1 < [ 2 ]     [ C / C++ (ANSI-Standard) ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: