nicht sicher bei delete



  • hi, ich probier gerad mit dynamischen mehrdimensionalen Arrays ein wenig herum und wollte den Speicher für die 2. Dimension, der ja oft grösser ist als bei der 1., zusammenfassen und nicht jede zweite Dimension einzeln einer ersten zuweisen. Dabei bin mir nicht so sicher, ob ich hier den Speicher wieder richtig freigebe:

    //ein Batzen Speicher für beide Dimensionen
    long* mem  = new long[dim1*dim2];
    memset(mem, 0, dim1*dim2*sizeof(long));
    
    //Speicher virtuelle erste Dimension
    long** ptrmem = new long*[dim1];
    
    //Dim1 Adressen aus Batzen zuweisen
    int i = 0;
    for(;i<dim1;++i)
       ptrmem[i] = &mem[i*dim2];
    //test ptrmem[x][y] = 10;
    
    //alles wieder löschen
    //is das überhaupt nötig, hier auf
    //NULL zu setzen?
    for(i = 0;i<DIM1;++i) 
       ptrmem[i] = NULL;
    //auch hier bin ich mir nicht sicher
    //ob del[] oder del
    delete ptrmem;
    ptrmem = NULL;
    delete[] mem;
    mem = NULL;
    


  • //alles wieder löschen
    //is das überhaupt nötig, hier auf
    //NULL zu setzen?
    for(i = 0;i<DIM1;++i)
       ptrmem[i] = NULL;
    

    Das ist eigentlich vollkommen unnötig und überflüssig, weil du den ganzen Speicher ja eh wieder freigibst und dann das Betriebssystem da dann ganz andere Werte als NULL reinschreiben kann.

    //auch hier bin ich mir nicht sicher
    //ob del[] oder del
    delete ptrmem;
    ptrmem = NULL;
    delete[] mem;
    mem = NULL;
    

    Hmmm, theoretisch genügt meiner Meinung nach ein "normales" delete. delete[] unterscheidet sich ja nur darin vom "normalen" delete, dass delete[] für jedes Objekt des Arrays den Destruktor aufruft, während das "normale" delete nur für das erste Objekt des Arrays den Destruktor aufruft. Beide Operatoren sollten aber den gleichen Speicherbereich wieder freigeben, und da du in deinem Beispiel nur mit elementaren Datentypen hantierst (die an sich keinen Konstruktor bzw. Destruktor haben), reicht theoretisch das "normale" delete.

    Allerdings heißt ja "genügen" nicht, dass man es nicht anders machen sollte. Ich persönlich würde auf jeden Fall ein delete[] verwenden, damit es im Quelltext offensichtlich wird, dass du Speicherplatz für ein Array freigibst und damit, falls du irgendwann mal den Datentyp des Arrays beim Umschreiben des Quelltextes ändern solltest, nicht vergisst beim delete die eckigen Klammern zu setzen.



  • Paul Panther schrieb:

    Hmmm, theoretisch genügt meiner Meinung nach ein "normales" delete. delete[] unterscheidet sich ja nur darin vom "normalen" delete, dass delete[] für jedes Objekt des Arrays den Destruktor aufruft, während das "normale" delete nur für das erste Objekt des Arrays den Destruktor aufruft. Beide Operatoren sollten aber den gleichen Speicherbereich wieder freigeben, und da du in deinem Beispiel nur mit elementaren Datentypen hantierst (die an sich keinen Konstruktor bzw. Destruktor haben), reicht theoretisch das "normale" delete.

    Falsch. Alles, was mit new[] angelegt wurde, muß mit delete[] wieder freigegeben werden - alle Versuche, new/delete und new[]/delete[] zu mischen, wären nicht portabel.



  • Die Deletes sehen schon gut aus, als Steigerung bietet es sich nun an dieses Array in eine Klasse zu kapseln und so ein "Intelligenten Zeiger" zu basteln - wo du im Destruktor dann alles nötige zerstörst.

    BTW, um alles auf null zu setzen gibt’s die nette Funktion "SecureZeroMemory" da brauchst du keine Schleife.
    (Aber für deine zwecke in dem Beispiel brauchst du nichts auf NULL setzen wie mein Vorredner schon anmerkte.)



  • Mr Evil schrieb:

    BTW, um alles auf null zu setzen gibt’s die nette Funktion "SecureZeroMemory" da brauchst du keine Schleife.

    Wenn schon, dann memset. WinAPI != C++.



  • pock schrieb:

    Mr Evil schrieb:

    BTW, um alles auf null zu setzen gibt’s die nette Funktion "SecureZeroMemory" da brauchst du keine Schleife.

    Wenn schon, dann memset. WinAPI != C++.

    Wenn schon C++, warum dann nicht Sprachmittel?

    long* mem  = new long[dim1*dim2] (); // fertig
    


  • LordJaxom schrieb:

    Wenn schon C++, warum dann nicht Sprachmittel?

    Weil es um diesen Abschnitt geht:

    int i = 0;
    for(i = 0;i<DIM1;++i)
       ptrmem[i] = NULL;
    


  • LordJaxom schrieb:

    long* mem  = new long[dim1*dim2] (); // fertig
    

    Und das schluckt der Compiler? Btw wäre mir neu, daß man bei new[] zusätzlich Ctor-Parameter mitgeben kann.



  • pock schrieb:

    Weil es um diesen Abschnitt geht:

    int i = 0;
    for(i = 0;i<DIM1;++i)
       ptrmem[i] = NULL;
    

    Ist mir schon klar. Der Sinn war ja auch dass dieser Abschnitt unnötig wird.

    CStoll schrieb:

    Und das schluckt der Compiler? Btw wäre mir neu, daß man bei new[] zusätzlich Ctor-Parameter mitgeben kann.

    Sind ja auch keine Parameter. IMHO kann man so die Default-Initialisierung für Arrays erzwingen. Wie bei int i(), wonach i garantiert 0 ist.

    Das schlucken Comeau, MSVC und GCC, und der MSVC verhält sich auch so dass mit Klammern alle Elemente des Arrays auf 0 gesetzt sind. (Der GCC auch, aber der macht es auch ohne Klammern, beim MSVC steht dann Schrott drin).



  • LordJaxom schrieb:

    Ist mir schon klar.

    Ich fürchte nicht.

    Der Sinn war ja auch dass dieser Abschnitt unnötig wird.

    Der Abschnitt wird aber durch dein new[] nicht unnötig. Es geht darum, ein bereits bestehendes Array mit 0 zu füllen. Du beziehst dich auf ein anderes Stück Code als ich.

    //alles wieder löschen
    //is das überhaupt nötig, hier auf
    //NULL zu setzen?
    for(i = 0;i<DIM1;++i)
       ptrmem[i] = NULL;
    

    Ich sage: es ist nicht nötig, aber wenn mans trotzdem macht ist memset wohl sinnvoller als irgendwelche WinAPI-Funktionen.



  • Hast recht, ich war in nem anderen Block.
    (Gibt's hier kein Verstecksmiley?)



  • CStoll schrieb:

    Falsch. Alles, was mit new[] angelegt wurde, muß mit delete[] wieder freigegeben werden - alle Versuche, new/delete und new[]/delete[] zu mischen, wären nicht portabel.

    Ist das sicher? Also in meinem Buch und in diesem Tutorial z.B. steht es anders http://www.cpp-tutor.de/cpp/le11/le11_01.htm#delete

    Und zumindest mein Buch, schmückt sich damit standard-konform zu sein...



  • Paul Panther schrieb:

    Ist das sicher? Also in meinem Buch und in diesem Tutorial z.B. steht es anders http://www.cpp-tutor.de/cpp/le11/le11_01.htm#delete

    In diesem Tutorial steht exakt das drin, was CStoll auch gesagt hat!?



  • Ja, ist sicher:
    (ein Zitat aus dem verlinkten Tutorial):

    Wurde dagegen Speicher für ein Feld reserviert, so muss nach dem delete Operator zunächst eine leere eckige Klammer stehen und erst danach der von new zurückgelieferte Zeiger. [...] Vergessen Sie daher niemals die eckigen Klammer beim delete von Feldern.

    In vielen Fällen verwenden new und new[] tatsächlich die selbe Speicherverwaltung, dann macht es nichts aus, daß du die [] weglässt. Aber das ist keineswegs garantiert (schließlich braucht new[] noch zusätzlichen Verwaltungsaufwand, um die Arraygröße zu notieren).

    Eine Beispiel-Implementation:

    void* operator new(size_t s)
    {
      return malloc(s);
    }
    
    void operator delete(void* p)
    {
      free(p);
    }
    
    void* operator new[](size_t s)
    {
      char* p=malloc(s+sizeof(size_t));
      *((int*)p) = s/typesize;//damit beim delete[] nachgesehen werden kann, wieviele Objekte ich löschen muß
      return p+sizeof(size_t);
    }
    
    void* operator delete[](void* p)
    {
      free((char*)p-sizeof(size_t));
    }
    

    wenn du jetzt einen Zeiger von new[] an delete weitergibst, hast du gute Chancen auf eine Access Violation



  • Paul Panther schrieb:

    ... und in diesem Tutorial z.B. steht es anders http://www.cpp-tutor.de/cpp/le11/le11_01.htm#delete

    Dann muss sich das Tutorial aber zwischen deinem Post und meinem Lesen geändert haben. Dort steht es genauso wie von CStoll gesagt.

    Und ja - das ist sicher. GCC-Executables schmieren dann schon mal gerne mit der Meldung mismatched delete ab.



  • - EDIT: Hat sich erledigt.



  • Vergessen Sie diese leere eckige Klammer, so meldet Ihnen der Compiler keinen Fehler! Bei Felder von einfachen Datentypen, d.h. keine Objekte, wird der reservierte Speicher auch richtig freigegeben.

    Hab' mich auf das Zitat bezogen und es geht ja immoment nur um elementare Datentypen...



  • Paul Panther schrieb:

    Vergessen Sie diese leere eckige Klammer, so meldet Ihnen der Compiler keinen Fehler! Bei Felder von einfachen Datentypen, d.h. keine Objekte, wird der reservierte Speicher auch richtig freigegeben.

    Hab' mich auf das Zitat bezogen und es geht ja immoment nur um elementare Datentypen...

    ... na ich lese da ganz klar eine Warnung, es nicht so zu machen. Immerhin weist er (mit Ausrufezeichen) darauf hin, dass der Compiler nicht warnt und bei primitiven Typen der Fehler evtl. nicht auffällt und nicht als Anleitung, es so zu machen.

    Gruß,

    Simon2.



  • Hmmm, er sagt ja aber eigentlich eindeutig, dass der Speicherbereich richtig freigegeben wird. Obwohl ich zugeben muss, dass er sich eigentlich widerspricht, weil er ja vorher sagt, dass nach new[] ein delete[] stehen muss.

    Bei meinem ersten Post in diesem Thread ging es mir ja auch eigentlich darum, herauszustellen, dass bei dem Beispiel des Threaderstellers meiner Meinung nach kein delete[] stehen muss, sondern dass ein delete[] stehen sollte.

    Allerdings bin ich mir jetzt nach dieser Diskussion irgendwie nicht mehr sicher...



  • Der Automechaniker sagt: "Ja, sie sind noch richtig bis hierhergekommen, aber sie sollten mit dem Auto nicht mehr fahren !" ...
    ICH würde mich selbst nicht mehr ans Steuer setzen und auch keinem Anderen raten, es zu tun. 😉

    Gruß,

    Simon2.



  • Informatik ist aber eine Wissenschaft und da sollte man meiner Meinung nach schon mit so Wörtern wie "müssen", "dürfen" und "sollen" anders umgehen. 😉 Die frage ist ja, was passiert, wenn der Automechaniker sagt, dass er für die Reperatur 2000 Euro verlangt und eine Werkstatt zwei Kilometer weiter entfernt verlangt nur 50 Euro. Wenn mir dann der Mechaniker sagt, ich sollte nicht mehr fahren, dann würde ich wahrscheinlich trotzdem noch die zwei Kilometer fahren. Sagt der Mechaniker allerdings, ich darf nicht mehr fahren und muss bei der Polizei 4000 Euro Strafe zahlen, wenn ich trotzdem weiter fahre, dann würde ich es mir schon überlegen.

    Wollte folgenden Link nur noch mal hinzufügen, da hier im selben Tutorial delete[] für Objektfelder erklärt wird... http://www.cpp-tutor.de/cpp/le11/le11_02.htm#objektfelder
    Und hier wird auch wieder nur auf die Destruktoraufrufe aufmerksam gemacht, nicht aber, dass delete und delete[] uterschiedlich viel speicher freigeben würden.

    Wie gesagt, mir ging es ja nur darum, ob delete und delete[] für Felder von elementaren Datentypen nicht im Endeffekt das gleiche machen (sollten, wenn der Compiler Standardkonform ist)? Ich habs auf jeden Fall immer so verstanden und les' es auch aus diesem Tutorial heraus und auch noch immer aus meinem Buch.


Anmelden zum Antworten