delete [] p ?



  • Ich glaub das ist ja eben meine Frage hier 🙂



  • Ehm würde mich mal interessieren was passiert wenn du folgendes machst:

    char* arr_ptr = new char[128];
    arr_ptr[127] = 0;
    
    delete arr_ptr;
    delete [] arr_ptr;
    

    ich würde glaube ich erwarten dass er folgendes macht(hab aber noch nicht wirklich drüber nachgedacht:

    delete &arr_ptr[0];
    delete [] arr_ptr; // BUMM! ;)
    

    also praktisch das 1. Element des Arrays löscht.

    Person* ptr = new Person(name, age);
    ptr->print();
    delete ptr;
    

    ... sieht nach dem Tippfehler aus ...



  • Ja aber drunter steht noch

    Zweimal Tippfehler?



  • Oder einfach ein Denkfehler. Auf jeden Fall müssen mit "new" erzeugte Objekte mit "delete" freigegeben werden, frei von irgendwelchen eckigen Klammern.

    Achso, und was das soll: delete bzw delete[] gibt nicht nur Speicher frei, sondern ruft ja auch den/die Destruktor/en auf. Bei delete[] weiß der Compiler halt, dass er von jedem Objekt den Destruktor aufrufen soll, nicht nur vom ersten. Und danach erst wird der Speicher freigegeben.



  • Hmm es ist nicht definiert. DU solltest das Buch wechseln 😉

    <a href= schrieb:

    IBM">The result of deleting an array object with delete is undefined, as is deleting an individual object with delete[]. The array dimensions do not need to be specified with delete[].



  • Ich sollte eher den Professor wechseln, denk ich 😃



  • Badestrand schrieb:

    Bei delete[] weiß der Compiler halt, dass er von jedem Objekt den Destruktor aufrufen soll, nicht nur vom ersten.

    aber wenn delete auf ein mit new[] erzeugtes array sowieso undefiniert ist, warum muss man denn dann explizit delete[] aufrufen?
    wenn der compiler bei delete[] weiß, wieviele felder freizugeben sind, müsste er es doch auch bei delete wissen können.



  • [][][] schrieb:

    aber wenn delete auf ein mit new[] erzeugtes array sowieso undefiniert ist, warum muss man denn dann explizit delete[] aufrufen?

    Weil der Compiler das an gewissen Stellen ja gar nicht wissen kann, ob der übergebene Zeiger auf ein Array zeigt oder auf ein einzelnes Objekt:

    void MyPersonalDeleteFunction( int* p )
    {
        delete p;
        // oder
        delete[] p;
        // ?
    }
    
    ..
    int* a = new int;
    int* b = new int[100];
    MyPersonalDeleteFunction( a );
    MyPersonalDeleteFunction( b );
    

    [][][] schrieb:

    wenn der compiler bei delete[] weiß, wieviele felder freizugeben sind, müsste er es doch auch bei delete wissen können.

    Da ist was wahres dran.. Allerdings wissen die Compiler oft mehr, als was der Standard vorgibt zu wissen.



  • Nun, er kompiliert und meckert nicht, wenn er hier ist...

    Person *p2 = new Person(name2, age);
    p2->print();
    delete [] p2;
    


  • Seikilos schrieb:

    Nun, er kompiliert und meckert nicht, wenn er hier ist...

    Das muss er auch nicht. Er könnte auch einen Sack Reis in Korea sprengen. Es ist und bleibt aber undefiniert, sprich illegal.



  • Ich freu mich schon drauf, dem Lehrer auf die Frage antworten zu können, dass das ein Fehler ist... 😃



  • [][][] schrieb:

    wenn der compiler bei delete[] weiß, wieviele felder freizugeben sind, müsste er es doch auch bei delete wissen können.

    Die Verwaltung der Arraylänge kostet zusätzlichen Aufwand, den kann sich der Compiler sparen, wenn er "weiß", daß er mit Einzelobjekten hantiert.

    Eine Beispiel-Implementierung (Semi-Pseudocode):

    //op new - liefert Speicher für ein Objekt (size Bytes)
    void* operator new(size_t size)
    {
      return malloc(size);
    }
    void operator delete(void* p)
    {
      free(p);
    }
    
    //op new[] - liefert Speicher für ein Array (size Bytes gesamt)
    void* operator new[](size_t size)
    {
      char* ptr = malloc(size+sizeof(size_t));
      *((int*)ptr) = size;
      return ptr+sizeof(size_t);
    }
    void operator delete[](void*p)
    {
      free((char*)p-sizeof(size_t));
    }
    


  • CStoll schrieb:

    Die Verwaltung der Arraylänge kostet zusätzlichen Aufwand, den kann sich der Compiler sparen, wenn er "weiß", daß er mit Einzelobjekten hantiert.

    dein beispiel zeigt aber, wie gering der zusätzliche aufwand wäre - im vergleich zu dem gewinn an konsistenz und dem verschwinden einer bösen potenziellen fehlerquelle, wenn delete[] wegfiele.

    Badestrand schrieb:

    Weil der Compiler das an gewissen Stellen ja gar nicht wissen kann, ob der übergebene Zeiger auf ein Array zeigt oder auf ein einzelnes Objekt:

    wenn er über die größe des speicherbereichs buch führen kann, könnte er doch genauso gut auch über die objektanzahl buch führen?



  • [][][] schrieb:

    wenn er über die größe des speicherbereichs buch führen kann, könnte er doch genauso gut auch über die objektanzahl buch führen?

    Der, der über die Größe des Speichers Buch führt, ist aber möglicherweise (bzw. meistens) jemand anderes, als derjenige, der sich um die Destruktion kümmert. Wenn z.B. die Operatoren new und delete auf malloc und free zurückgreifen, ist ihnen die Größe des Speichers egal, darum kümmern sich malloc/free. Die Destruktion von Objekten muss der C++-Teil übernehmen, dafür ist aber nur die Anzahl erforderlich. (Wenngleich man natürlich zwischen Anzahl und Größe umrechnen kann).



  • [][][] schrieb:

    CStoll schrieb:

    Die Verwaltung der Arraylänge kostet zusätzlichen Aufwand, den kann sich der Compiler sparen, wenn er "weiß", daß er mit Einzelobjekten hantiert.

    dein beispiel zeigt aber, wie gering der zusätzliche aufwand wäre - im vergleich zu dem gewinn an konsistenz und dem verschwinden einer bösen potenziellen fehlerquelle, wenn delete[] wegfiele.

    Es gibt verschiedene Möglichkeiten, die Größe des Arrays zu verwalten (u.a. auch über eine map<void*,size_t> oder per Zugriff auf die Verwaltungsdaten von malloc/free) - die jeweils ihre eigenen Vor- und Nachteile haben. Auf jeden Fall muß new[] diese Anzahl irgendwo hinterlegen und das kostet Platz* (und Rechenzeit).

    * Bei kleinen Objekten kann es durchaus vorkommen, daß du damit deinen speicherbedarf verdoppelst.



  • LordJaxom schrieb:

    Der, der über die Größe des Speichers Buch führt, ist aber möglicherweise (bzw. meistens) jemand anderes, als derjenige, der sich um die Destruktion kümmert. Wenn z.B. die Operatoren new und delete auf malloc und free zurückgreifen, ist ihnen die Größe des Speichers egal, darum kümmern sich malloc/free. Die Destruktion von Objekten muss der C++-Teil übernehmen, dafür ist aber nur die Anzahl erforderlich. (Wenngleich man natürlich zwischen Anzahl und Größe umrechnen kann).

    das überzeugt mich nicht. delete kennt die größe des speicherbereichs. delete[] kennt außerdem die anzahl der zu zerstörenden objekte. das heißt, dass beide informationen irgendwo abgelegt sein müssen, und der zugrunde liegende code weiß, wie er an diese informationen heran kommt.
    wenn er diese zwei size_t-variablen mit jedem new[] ablegen kann, warum dann nicht noch ein zusätzliches bit, um automatisch zwischen new und new[] zu unterscheiden?

    mal ne andere frage: wenn ich mein eigenes delete schreiben würde, könnte ich es theoretisch so schreiben, dass es ein notwendiges delete[] selbst erkennen und durchführen kann?



  • new/delete braucht die Information nicht, wieviele Elemente das Array nun hat (es ist immer genau 1), also wozu sollte es diese Information noch speichern? Das kostet nur Speicherplatz und Rechenzeit.

    PS: Wie du deine eigenen new/delete's implementierst, ist natürlich dir selber überlassen - der ANSI-Standard legt nur fest, daß es undefiniert ist, wenn du Array- und Single-Operatoren mischst.



  • CStoll schrieb:

    new/delete braucht die Information nicht, wieviele Elemente das Array nun hat (es ist immer genau 1), also wozu sollte es diese Information noch speichern? Das kostet nur Speicherplatz und Rechenzeit.

    es sollte diese information speichern, weil eine unterscheidung zwischen delete und delete[] fehleranfällig ist und weil es die syntax komplizierter als notwendig macht.

    die erhöhte rechenzeit zur abfrage eines bits halte ich für kaum relevant, wenn man den üblichen aufwand für eine (de-)allokation in relation dazu setzt.
    der speicherbedarf stiege in der tat an, günstigenfalls aber nur um ein bit (nicht gesetzt = nur ein element), das sich auf vielen plattformen bestimmt noch in den ohnehin vorhandenen verwaltungsinformationen unterbringen ließe.

    und für sehr kleine allokationen könnte man ja immer noch in eigener verantwortung auf malloc und free zurückgreifen.

    gibt es wirklich keine besseren gründe für eine unterscheidung außer den genannten?

    CStoll schrieb:

    der ANSI-Standard legt nur fest, daß es undefiniert ist, wenn du Array- und Single-Operatoren mischst.

    die frage war: ist es praktisch möglich, delete so zu implementieren, dass es ein notwendiges delete[] selbst erkennt und ausführt? es also egal ist, ob man delete oder delete[] aufruft?



  • [][][] schrieb:

    CStoll schrieb:

    new/delete braucht die Information nicht, wieviele Elemente das Array nun hat (es ist immer genau 1), also wozu sollte es diese Information noch speichern? Das kostet nur Speicherplatz und Rechenzeit.

    es sollte diese information speichern, weil eine unterscheidung zwischen delete und delete[] fehleranfällig ist und weil es die syntax komplizierter als notwendig macht.

    die erhöhte rechenzeit zur abfrage eines bits halte ich für kaum relevant, wenn man den üblichen aufwand für eine (de-)allokation in relation dazu setzt.
    der speicherbedarf stiege in der tat an, günstigenfalls aber nur um ein bit (nicht gesetzt = nur ein element), das sich auf vielen plattformen bestimmt noch in den ohnehin vorhandenen verwaltungsinformationen unterbringen ließe.

    Ein einzelnes Bit kann nicht alleine untergebracht werden, also mußt du mindestens ein Byte für die Information "ich bin ein Array" veranschlagen - aus Padding-Gründen meist noch mehr. Und dieses mußt du so anlegen, daß es nicht mit irgendwelchen Nutz-Informationen kollidiert.

    und für sehr kleine allokationen könnte man ja immer noch in eigener verantwortung auf malloc und free zurückgreifen.

    Und auf die Vorteile von new/delete (automatische Ctor/Dtor-Aufurfe etc) verzichten? Nein danke.

    gibt es wirklich keine besseren gründe für eine unterscheidung außer den genannten?

    Wenn dir die genannten Gründe nicht gut genug sind, bist du selber Schuld 😉 Mir reichen sie.

    CStoll schrieb:

    der ANSI-Standard legt nur fest, daß es undefiniert ist, wenn du Array- und Single-Operatoren mischst.

    die frage war: ist es praktisch möglich, delete so zu implementieren, dass es ein notwendiges delete[] selbst erkennt und ausführt? es also egal ist, ob man delete oder delete[] aufruft?

    Man könnte die Aufrufe von der Single Operatoren auf die entsprechenden Array-Operatoren umbiegen, das löst zumindest die Speicherverteilung. Aber ob man das Verhalten von new/delete direkt beeinflussen kann, bin ich mir nicht sicher (delete ruft einen Dtor auf und übergibt anschließend den Speicher an op delete; delete[] ermittelt die Arraygröße, ruft n Dtoren auf und übergibt den Speicher an op delete[]).



  • CStoll schrieb:

    Ein einzelnes Bit kann nicht alleine untergebracht werden, also mußt du mindestens ein Byte für die Information "ich bin ein Array" veranschlagen - aus Padding-Gründen meist noch mehr. Und dieses mußt du so anlegen, daß es nicht mit irgendwelchen Nutz-Informationen kollidiert.

    praktisch wirst du auf den meisten plattformen für dieses eine bit den platz finden. gibt es überhaupt eine plattform, auf der ein new[std::numeric_limits<size_t>::max() >> 1] nicht fehlschlägt?e

    CStoll schrieb:

    und für sehr kleine allokationen könnte man ja immer noch in eigener verantwortung auf malloc und free zurückgreifen.

    Und auf die Vorteile von new/delete (automatische Ctor/Dtor-Aufurfe etc) verzichten? Nein danke.

    das ist doch praktisch überhaupt nicht relevant. wie oft legst du so kleine objekte mit new an, dass der entstehende overhead für die anzahl überhaupt ins gewicht fällt?

    Wenn dir die genannten Gründe nicht gut genug sind, bist du selber Schuld 😉 Mir reichen sie.

    vielleicht ist mir ist konsistenz und sicherer code wichtiger als dir.
    ich gebe allerdings zu, von den heutigen maßstäben bei speicherausbau und rechengeschwindigkeit verwöhnt zu sein. allerdings wird auf einem rechner der c64-klasse c++ (oder dynamische speicherallokation überhaupt) nicht sehr sinnvoll sein.


Anmelden zum Antworten