Warning beim deleten von Zeiger Array



  • Warum kommt da ein "warning"?

    #include <iostream>
    using namespace std;
    
    class Mammal
    {
        public:
        Mammal();
        ~Mammal();
        inline virtual void Speak() const {cout << "Mammal spricht\n";}
    
        private:
        int m_Age;
    };
    
    Mammal::Mammal()
    :m_Age(12)
    {
        cout << "Mammal Konstruktor\n";
    }
    
    Mammal::~Mammal()
    {
        cout << "Mammal Destruktor\n";
    }
    
    class Cat : public Mammal
    {
        public:
        Cat();
        ~Cat();
        inline void Speak() const {cout << "Miau\n";}
        inline void Purr() const {cout << "rrrrrr\n";}
    
        private:
    };
    
    Cat::Cat()
    {
        cout << "Cat Konstruktor\n";
    }
    
    Cat::~Cat()
    {
        cout << "Cat Destruktor\n";
    }
    
    class Dog : public Mammal
    {
        public:
        Dog();
        ~Dog();
        inline void Speak() const {cout << "Wuff\n";}
    
        private:
    };
    
    Dog::Dog()
    {
        cout << "Dog Konstruktor\n";
    }
    Dog::~Dog()
    {
        cout << "Dog Destruktor\n";
    }
    
    int main()
    {
        const size_t NUM_MAMMAL=3;
        Mammal *Zoo[3];
        for (int i=0;i<NUM_MAMMAL;i++)
        {
            int choice;
            cout << "(1) <- Katze      (2) <- Hund\n";
            cin >> choice;
            switch (choice)
            {
                case 1:
                Zoo[i]=new Cat;
                break;
    
                case 2:
                Zoo[i]=new Dog;
                break;
            }
        }
        cout << "\n\n\n";
        for (int i=0;i<NUM_MAMMAL;i++)
        {
            Zoo[i]->Speak();
            Cat *temp=dynamic_cast<Cat *> (Zoo[i]);
            if (!temp)
            {
                cout << "Leider keine Katze\n";
            }
            else
            {
                temp->Purr();
            }
            delete temp;
        }
        delete [] Zoo; // <-- Zeile des Unheils
    
        return 0;
    }
    

    COMPILERMELDUNG:

    Compiling: C:\MinGW\Andi\kapitel 18.cpp
    C:\MinGW\Andi\kapitel 18.cpp: In function int main()': C:\\MinGW\\Andi\\kapitel 18.cpp:105: warning: deleting arrayMammal*Zoo[3]'
    Linking console executable: C:\MinGW\Andi\kapitel 18.exe
    Process terminated with status 0 (0 minutes, 0 seconds)
    0 errors, 1 warnings

    Checking for existence: C:\MinGW\Andi\kapitel 18.exe
    Executing: C:\MinGW/cb_console_runner.exe "C:\MinGW\Andi\kapitel 18.exe" (in C:\MinGW\Andi)
    Process terminated with status -1073741510 (0 minutes, 10 seconds)

    MfG
    Stromberg



  • Dein Array liegt auf dem Stack, das darfst du gar nicht löschen.
    Du schreibst ja auch nicht das hier:

    int i;
    delete &i;
    

    Eigentlich willst du nur die einzelnen Elemente löschen.



  • Mh, also so is es dann richtig:

    #include <iostream>
    using namespace std;
    
    class Mammal
    {
        public:
        Mammal();
        ~Mammal();
        inline virtual void Speak() const {cout << "Mammal spricht\n";}
    
        private:
        int m_Age;
    };
    
    Mammal::Mammal()
    :m_Age(12)
    {
        cout << "Mammal Konstruktor\n";
    }
    
    Mammal::~Mammal()
    {
        cout << "Mammal Destruktor\n";
    }
    
    class Cat : public Mammal
    {
        public:
        Cat();
        ~Cat();
        inline void Speak() const {cout << "Miau\n";}
        inline void Purr() const {cout << "rrrrrr\n";}
    
        private:
    };
    
    Cat::Cat()
    {
        cout << "Cat Konstruktor\n";
    }
    
    Cat::~Cat()
    {
        cout << "Cat Destruktor\n";
    }
    
    class Dog : public Mammal
    {
        public:
        Dog();
        ~Dog();
        inline void Speak() const {cout << "Wuff\n";}
    
        private:
    };
    
    Dog::Dog()
    {
        cout << "Dog Konstruktor\n";
    }
    Dog::~Dog()
    {
        cout << "Dog Destruktor\n";
    }
    
    int main()
    {
        const size_t NUM_MAMMAL=3;
        Mammal *Zoo[3];
        for (int i=0;i<NUM_MAMMAL;i++)
        {
            int choice;
            cout << "(1) <- Katze      (2) <- Hund\n";
            cin >> choice;
            switch (choice)
            {
                case 1:
                Zoo[i]=new Cat;
                break;
    
                case 2:
                Zoo[i]=new Dog;
                break;
            }
        }
        cout << "\n\n\n";
        for (int i=0;i<NUM_MAMMAL;i++)
        {
            Zoo[i]->Speak();
            Cat *temp=dynamic_cast<Cat *> (Zoo[i]);
            if (!temp)
            {
                cout << "Leider keine Katze\n";
            }
            else
            {
                temp->Purr();
            }
            delete temp;
            delete Zoo[i];
        }
    
        return 0;
    }
    

    MfG
    Stromberg



  • delete gehört zu new,
    delete[] gehört zu new[].

    char* puffer = new char[1024];
    // irgendwas tolles machen
    delete[] puffer;
    


  • Du brauchst das

    delete temp;
    

    nicht. temp ist ja nur eine gecastete Version des Original-Objekts im Array.

    Eine bessere Version wäre:

    #include <iostream>
    using namespace std;
    
    class Mammal
    {
        public:
            Mammal();
            virtual ~Mammal(); //virtual hinzugefügt
            inline virtual void Speak() const {cout << "Mammal spricht\n";}
        private:
            int m_Age;
    };
    
    Mammal::Mammal() : m_Age(12)
    {
        cout << "Mammal Konstruktor\n";
    }
    
    Mammal::~Mammal()
    {
        cout << "Mammal Destruktor\n";
    }
    
    class Cat : public Mammal
    {
        public:
            Cat();
            virtual ~Cat(); //virtual hinzugefügt
            inline void Speak() const {cout << "Miau\n";}
            inline void Purr() const {cout << "rrrrrr\n";}
        private:
    };
    
    Cat::Cat()
    {
        cout << "Cat Konstruktor\n";
    }
    
    Cat::~Cat()
    {
        cout << "Cat Destruktor\n";
    }
    
    class Dog : public Mammal
    {
        public:
            Dog();
            virtual ~Dog(); //virtual hinzugefügt
            inline void Speak() const {cout << "Wuff\n";}
        private:
    };
    
    Dog::Dog()
    {
        cout << "Dog Konstruktor\n";
    }
    Dog::~Dog()
    {
        cout << "Dog Destruktor\n";
    }
    
    int main()
    {
        const size_t NUM_MAMMAL=3;
        Mammal *Zoo[NUM_MAMMAL]; //wieso benutzt du hier vorher 3 und nicht NUM_MAMMAL?
        for (int i=0;i<NUM_MAMMAL;i++)
        {
            int choice;
            cout << "(1) <- Katze      (2) <- Hund\n";
            cin >> choice;
            switch (choice)
            {
                case 1:
                    Zoo[i]=new Cat;
                    break;
    
                case 2:
                    Zoo[i]=new Dog;
                    break;
            }
        }
        cout << "\n\n\n";
        for (int i=0;i<NUM_MAMMAL;i++)
        {
            Zoo[i]->Speak();
            Cat *temp=dynamic_cast<Cat *> (Zoo[i]);
            if (!temp)
            {
                cout << "Leider keine Katze\n";
            }
            else
            {
                temp->Purr();
            }
            //delete temp; Die Zeile ist nicht notwendig.
            delete Zoo[i];
        }
        return 0;
    }
    

    Zur Begründung: delete temp ist unnötig/sogar falsch, da es vorkommen kann, dass du ein bereits gelöschtes Objekt (falls es eine Katze war) löschst und dann mit delete Zoo[i]; nochmal löschen willst ➡ du zerschießt dir deinen Heap/undefiniertes Verhalten.

    Zu den virtuellen Destruktoren (eigtl. hätte man die nur in der Basisklasse virtuell deklarieren müssen, da es in den abgeleiteten Klassen dann implitzit geschieht, aber so ist es halt nocheinmal explizit): Als Faustregel sollte jede Klasse mit mindestens einer virtuellen Methode einen virtuellen Destruktor haben, da sonst bei einem delete über einen Zeiger der Basisklasse nur der Destruktor der Basisklasse aufgerufen wird, was zu Fehlern führt, wenn eine abgeleitete Klasse Ressourcen wie Speicher oder Dateien allokiert.

    Und natürlich sollte man dynamic_cast wenn möglich meiden (weist auf schlechtes Design hin ;). Aber das Beispiel war ja nur dazu da, dynamic_cast auszuprobieren.

    Felix



  • Achso, man soll "temp" nicht deleten. Ich hab mir das nämlich imemr so gedacht, das der dynamic_cast halt für delete Speicher reserviert, und in diesen dann den Cat Teil reinpresst. Aber scheinend ist es so das gar kein neuer Speicher reserviert wird, sondern das "temp" dann irgendwie auf die gleiche Adresse wie Cat zugreift? Oder wie genau funktioniert dass, oder ist es zu komplex?

    TomasRiker schrieb:

    delete gehört zu new,
    delete[] gehört zu new[].

    char* puffer = new char[1024];
    // irgendwas tolles machen
    delete[] puffer;
    

    Okay, die Regel hab ich verstanden, aber wir greife ich mit "puffer" jetzt z.B. auf "Feld 235" zu? Ich kann mich noch dunkeln erinnern das es da was gab, das wenn man "++" macht das es ein Feld hoch geht, und bei "--" eins runter? Kann ich auch einfach z.B. "puffer[235]" machen? ....Kann mir das mal nochmal jemand erklären, ich nutze sowas so selten irgendwie..

    MfG
    Stromberg



  • Stromberg schrieb:

    Ich kann mich noch dunkeln erinnern das es da was gab, das wenn man "++" macht das es ein Feld hoch geht, und bei "--" eins runter? Kann ich auch einfach z.B. "puffer[235]" machen? ....Kann mir das mal nochmal jemand erklären, ich nutze sowas so selten irgendwie..

    wenn du einen *char puffer = new char[1024]; hast, dann kannst du mit puffer[0]...puffer[1023] bzw. *puffer...*(puffer+1023) auf die einzelnen elemente zugreifen.
    übrigens, falls du den pointer hoch- oder runterzählen willst: du brauchst den originalwert für's delete d.h. du solltest irgendwie dafür sorgen, dass der dabei nicht verloren geht.
    🙂



  • Stromberg schrieb:

    Achso, man soll "temp" nicht deleten. Ich hab mir das nämlich immer so gedacht, das der dynamic_cast halt für delete Speicher reserviert, und in diesen dann den Cat Teil reinpresst. Aber scheinend ist es so das gar kein neuer Speicher reserviert wird, sondern das "temp" dann irgendwie auf die gleiche Adresse wie Cat zugreift? Oder wie genau funktioniert dass, oder ist es zu komplex?

    dynamic_cast überprüft anhand des Eintrags in der v-table, ob der Cast zur abgeleiteten Klasse legal ist (d.h. ob das Objekt, auf das gezeigt wird überhaupt eine Instanz der abgeleiteten Klasse ist) und gibt dann einen Zeiger auf die gleiche Adresse zurück (oder 0, falls der Cast nicht legal war). Das heißt, du darfst nur einen der beiden Zeiger deleten. Wenn du virtuelle Destruktoren benutzt (und das solltest du), ist es egal welcher von beiden Zeiger verwendet wird, da aber der Original-Zeiger auch für Objekte, die keine Instanz von Cat sind gelöscht werden, ist es einheitlicher, delete auf den nicht-gecasteten Zeiger anzuwenden.

    Ich hoffe, man versteht diesen Bandwurmsatz 😛

    Felix



  • Undertaker schrieb:

    übrigens, falls du den pointer hoch- oder runterzählen willst: du brauchst den originalwert für's delete d.h. du solltest irgendwie dafür sorgen, dass der dabei nicht verloren geht.

    Gut das du mir dass sagst. Aber schätzungweiße ich hab jetzt schon so viel mit "++" u. "--" rumgespielt, und ich gar kein plan mehr habe wo ich mich im Fled befinde, und wie ich wieder zum anfang zurückkomme, kann ich dann nicht einfach:

    delete [](puffer[0]);
    

    machen? Oder geht das nicht?

    @Phoemuex
    Hab ich verstanden. Danke.

    MfG
    Stromberg



  • Stromberg schrieb:

    ... kann ich dann nicht einfach:

    delete [](puffer[0]);
    

    machen? Oder geht das nicht?

    so gehts

    delete [] &puffer[0];
    oder
    delete [] puffer;
    😉



  • Stromberg schrieb:

    Undertaker schrieb:

    übrigens, falls du den pointer hoch- oder runterzählen willst: du brauchst den originalwert für's delete d.h. du solltest irgendwie dafür sorgen, dass der dabei nicht verloren geht.

    Gut das du mir dass sagst. Aber schätzungweiße ich hab jetzt schon so viel mit "++" u. "--" rumgespielt, und ich gar kein plan mehr habe wo ich mich im Fled befinde, und wie ich wieder zum anfang zurückkomme, kann ich dann nicht einfach:

    einfach die adresse zwischenspeichern. guckst du:

    char *puffer = new char[...];
    char *saved = puffer;
    /*
     * hier irgendwie wild 'puffer' verändern
     */
    delete[] saved; // <-- voilà tout
    

    🙂



  • @Undertaker
    Okay, is klar so mach ich das dann wohl. Aber am besten wirs einfach sein wenn ich nen Überlick darüber behalte wo ich mich gerade im Feld befinde.

    ohaoha schrieb:

    delete [] &puffer[0];
    oder
    delete [] puffer;

    Also das mit "delete [] puffer" hab ich ja gemeint, aber das funzt ja nicht mehr wenn ich kein plan habe wo ich mich im Feld befinde.
    Und "delete [] &puffer[0]", mh da muss ich nochmal drüber nachdenken, des check ich noch nicht so ganz. Brauch bisschen Nachhilfe, also wenn ich nen Zeiger habe: int *temp; Der reservierten Speicher beinhaltet: int *temp=new int;

    cout << temp << endl; // Jetzt wird doch die Adresse von dem reservierten Speicher ausgegeben oder?
    
    cout << &temp << endl; // Jetzt wird doch die Adresse von dem Zeiger "temp" ausgegeben oder?
    
    //Oder ist es umgekehrt?
    

    Was passiert dann in dem Fall "delete [] &puffer[0]; " von "ohaoha"? Weil "&puffer" müsste ja dann die Adresse des Zeigers sein? Oder verhält sich das in diesem Fall irgendwie anderst? Bin ich verwirrt?

    MfG
    Stromberg



  • Stromberg schrieb:

    @Undertaker
    Okay, is klar so mach ich das dann wohl. Aber am besten wirs einfach sein wenn ich nen Überlick darüber behalte wo ich mich gerade im Feld befinde.

    ohaoha schrieb:

    delete [] &puffer[0];
    oder
    delete [] puffer;

    Also das mit "delete [] puffer" hab ich ja gemeint, aber das funzt ja nicht mehr wenn ich kein plan habe wo ich mich im Feld befinde.
    Und "delete [] &puffer[0]", mh da muss ich nochmal drüber nachdenken, des check ich noch nicht so ganz. Brauch bisschen Nachhilfe, also wenn ich nen Zeiger habe: int *temp; Der reservierten Speicher beinhaltet: int *temp=new int;

    cout << temp << endl; // Jetzt wird doch die Adresse von dem reservierten Speicher ausgegeben oder?
    
    cout << &temp << endl; // Jetzt wird doch die Adresse von dem Zeiger "temp" ausgegeben oder?
    
    //Oder ist es umgekehrt?
    

    Was passiert dann in dem Fall "delete [] &puffer[0]; " von "ohaoha"? Weil "&puffer" müsste ja dann die Adresse des Zeigers sein? Oder verhält sich das in diesem Fall irgendwie anderst? Bin ich verwirrt?

    MfG
    Stromberg

    Ich ging davon aus, dass Du den puffer-Zeiger unverändert laesst und mit puffer[index++] durch Dein Feld gehst.
    puffer waere dann der unveraenderte Zeiger den new geliefert hat und den delete[] erwatet - puffer waere dann auch gleich &puffer[0] !
    oder in deinem Beispiel:

    int *temp=new int;
    

    Hier waeren temp und &temp[0] als Zeiger identisch.

    MfG


Anmelden zum Antworten