new[] delete wann error?



  • #include <iostream>
    #include <conio.h>
    using namespace std;
    
    class X
    {
        int a;
    };
    
    class Y
    {
        X x;
        int b;
    };    
    
    int main()
    {
        int n;
        cin >> n;
    
        cout << "Speicher wird angefordert." << endl;
        Y * pY = new Y[n];
        getch();
    
        cout << "Speicher wird frei gegeben." << endl;
        delete pY; // inkorrekt. richtig: delete[] pX 
        getch();
    }
    

    Trotz des falschen delete wird der Speicher korrekt frei gegeben. Wann genau geht das schief? 😕



  • muss das nen fehler geben? reicht es nicht schon, dass nicht der komplette speicher freigegeben wird?



  • Trotz des falschen delete wird der Speicher korrekt frei gegeben

    Woher willst du das wissen? Ist nur Zufall, dass das Programm an der Stelle nicht abstürzt.



  • Der Speicher wird komplett wieder frei gegeben (Kontrolle via Taskmanager - Systemleistung - Speichernutzung). Ich suche ein Programm, das eben nur das erste Arrayelement frei gibt, also den Speicher auch nach dem Beenden weiter blockiert. Bei einfachen Typen funktioniert die Freigabe auch mit delete anstelle delete[]. Wird nirgends sauber beschrieben, immer nur davor gewarnt. 😃



  • struct noisy {
     ~noisy () { cout << "~noisy\n"; }
    };
    
    int main() {
    	noisy *n = new noisy[20];
    	delete n;
    }
    

    geht das auch noch? wie erwartet?
    undefiniertes verhalten äußert sich eben manchmal nicht sofort.



  • davie schrieb:

    geht das auch noch? wie erwartet?

    Beim VC++ schon. Der kennt den Unterschied zwischen delete und delete[] nicht.



  • Wie soll der Speicher weiter "blockiert" sein, wenn der Prozess tot ist? Das kann nicht gehen. Was sollte das überhaupt für einen Sinn haben?



  • Shade Of Mine schrieb:

    Beim VC++ schon. Der kennt den Unterschied zwischen delete und delete[] nicht.

    liefert das auch die "erwartete" ausgabe:

    struct noisy {
      int filler;
      void operator delete [] (void *v, size_t size) {
        cout << "delete [] of noisy, size=" << size
    	 	   << " (" << size/sizeof(noisy) << " noisies)\n"; 
        ::operator delete [] (v);
      }
      void operator delete (void *v, size_t size) {
        cout << "delete of noisy, size=" << size << '\n'; 
        ::operator delete(v);
      }
      virtual ~noisy () { cout << "~noisy\n"; }
    };
    
    struct baby : noisy {
      int filler;
      void operator delete [] (void *v, size_t size) {
        cout << "delete [] of baby, size=" << size
    	      << " (" << size/sizeof(baby) << " babies)\n";  
        ::operator delete [] (v);
      }
      void operator delete (void *v, size_t size) {
        cout << "delete of baby, size=" << size << '\n'; 
        ::operator delete(v);
      }
    };
    
    int main() {
    /*	noisy *n = new noisy;
    	delete n;    //ok
    
    	n = new noisy [2];
    	delete [] n; //ok*/
    
    	n = new noisy [2];
    	delete n;    //schlecht
    
    /*	n = new baby;
    	delete n;    //ok*/
    
    	n = new baby [2];
    	delete [] n; //schlecht
    
    	n = new baby [2];
    	delete n;    //schlecht
    }
    


  • Ringding schrieb:

    Wie soll der Speicher weiter "blockiert" sein, wenn der Prozess tot ist? Das kann nicht gehen. Was sollte das überhaupt für einen Sinn haben?

    Solange der Prozess aber läuft, ist er NICHT Frei.

    Devil



  • Der Speicher wird komplett wieder frei gegeben (Kontrolle via Taskmanager - Systemleistung - Speichernutzung). Ich suche ein Programm, das eben nur das erste Arrayelement frei gibt, also den Speicher auch nach dem Beenden weiter blockiert. Bei einfachen Typen funktioniert die Freigabe auch mit delete anstelle delete[]. Wird nirgends sauber beschrieben, immer nur davor gewarnt.

    Viele Implementirungen machen keinen Unterschied zwischen delete und delete[] für Types ohne Dtor. Find ich eigentlich auch sinnlos da einen Unterschied zu machen aber es gibt ihn:
    http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.12

    In der Praxis sind Fälle in denen es nicht klapt aber äusserst selten.



  • Folgender Code zeigt die Schieflage, z.B. bei n=20000000. Einmal wird der Speicher frei gegeben, das andere Mal nicht. Einfach mit dem Taskmanager verfolgen.

    #include <iostream>
    #include <conio.h>
    
    using namespace std;
    
    bool printflag;
    
    class X
    {
      public:
      int * p_; 
      X()  
      {
         p_ = new int; // hier allokiert das Objekt eigenen Speicher 
      }
    
      ~X ()  
      {
          if(printflag)
              cout << "destructor\n";
          delete p_;
      }
    };
    
    int main()
    {
        unsigned int n;
        cout << "number of elements? ";
        cin  >> n;
        if(n<100)
            printflag = true;
        X * pA = new X[n];
        cout << "memory on heap allocated."  << endl << endl;
        cout << "first  element: " << pA     << endl;
        cout << "second element: " << pA+1   << endl;
        cout << "last   element: " << pA+n-1 << endl << endl;
    
        getch();
    
        //delete[] pA; //   korrekt
        delete pA;     // nicht korrekt
        getch();
    }
    

    Warum wird der Speicher so "schleppend" belegt? Merkwürdiges Verhalten. 😉

    Bei delete pA wird zwar der Speicher für das ganze Feld aufgegeben, jedoch nur für das erste Objekt der Destruktor aufgerufen. Das bedeutet, dass der Speicher verloren geht (memory leak), den das zweite bis letzte Objekt im Konstruktor angelegt haben. delete[] ruft den Destruktor für jedes Arrayelement auf. Dieser gibt den für das Objekt belegten Speicher wieder frei.



  • Bei delete pA wird zwar der Speicher für das ganze Feld aufgegeben, jedoch nur für das erste Objekt der Destruktor aufgerufen. Das bedeutet, dass der Speicher verloren geht (memory leak), den das zweite bis letzte Objekt im Konstruktor angelegt haben. delete[] ruft den Destruktor für jedes Arrayelement auf. Dieser gibt den für das Objekt belegten Speicher wieder frei.

    Es ging um:

    Bei einfachen Typen funktioniert die Freigabe auch mit delete anstelle delete[]. Wird nirgends sauber beschrieben, immer nur davor gewarnt

    Und ich glaube deine Erklärung könnte man unter:

    Wird nirgends sauber beschrieben, immer nur davor gewarnt

    einorden.

    Der einzige Grund wieso man delete[] statt delete (oder delete statt delete[]!) nehmen soll ist weil der Standard zuläst, dass operator delete und operator delete[] verschieden implementiert sein können. Sie sind es aber (meines Wissen nach) nie. Das einzige was der Standard garantiert ist, dass

    delete new int;
    delete[] new int[5];
    

    funzen muss.

    Also ist

    delete new [4];
    

    gleich schlimm wie:

    void main(void)
    

    Es ist nicht Standard aber jeder existierender Compiler kommt damit klar.



  • Hi!

    Irgendwer schrieb:

    Also ist

    delete new [4];
    

    gleich schlimm wie:

    void main(void)
    

    Es ist nicht Standard aber jeder existierender Compiler kommt damit klar

    Kommt nicht, wie z.B. der VC++, der akzeptiert das nicht (void main()).

    Code-Hacker



  • @irgendwer: warum liefert dann delete und delete[] im Bsp.von E.Henkes verschiedene Reaktionen, wenn diese gleich sind? Dein allgemeines Gerede kapier ich nicht. 😕



  • es ist einfach undefiniertes verhalten- basta.
    ärger bekommt man irgendwie schon noch 😉



  • @irgendwer: warum liefert dann delete und delete[] im Bsp.von E.Henkes verschiedene Reaktionen, wenn diese gleich sind?

    War die Frage jetzt ernst gemeint?

    Weil A in seinem Beispiel einen Dtor hat und es geht um dtorlose Type.



  • Kein Wunder, dass viele C++ den Rücken kehren, bei solchen Fallstricken und undefinierten Möglichkeiten.



  • lool so kann mans natürlich auch sehen 😉

    aber in nem großen code weis man bei einem delete[] sofort, dass damit ein array gemeint ist, und mit einem delete sieht man sofort dass ein einzelnes objekt gemeint ist, sowas fördert die übersichtlichkeit-selbst wenn dem compiler egal sit, welches delete man benutzt



  • otze schrieb:

    aber in nem großen code weis man bei einem delete[] sofort, dass damit ein array gemeint ist, und mit einem delete sieht man sofort dass ein einzelnes objekt gemeint ist, sowas fördert die übersichtlichkeit-selbst wenn dem compiler egal sit, welches delete man benutzt

    ich halte das aber für irrelevant ob ich ein Array oder ein normales Objekt zerstöre.

    Der Grund warum C++ da trennt ist einfach die Performance 😉



  • wat passiert eigentlich, wenn ich immer delete[] verwende? 😉
    wäre das sicher?



  • hyperschlau schrieb:

    wat passiert eigentlich, wenn ich immer delete[] verwende? 😉
    wäre das sicher?

    Nein. Es sei denn du wuerdest auch immer new[] verwenden.

    Aber das ganze ist doch irrelevant - da man sowieso kein falsches delete verwendet. Warum? Weil man Arrays so schoen in Klassen stecken kann...

    Abgesehen davon, ist ein delete mit delete[] verwechseln ein Anfaengerfehler, mehr nicht.


Anmelden zum Antworten