2017-12-07 5 views
-2

Angenommen, ich habe die folgenden Klassen und Objekte:Wie man richtig Zeiger löschen, wenn abstrakte Klassen mit

#include <iostream> 

class Animal 
{ 
public: 
    virtual void makeNoise() = 0; 

    void eat() 
    { 
     std::cout << "Eating..." << "\n"; 
    } 

    void sleep() 
    { 
     std::cout << "Sleeping..." << "\n"; 
    } 
}; 

class Cat: public Animal 
{ 
public: 
    void makeNoise() 
    { 
     std::cout << "Miow..." << "\n"; 
    } 
}; 

class Cow: public Animal 
{ 
public: 
    void makeNoise() 
    { 
     std::cout << "Mooo..." << "\n"; 
    } 
}; 

int main() 
{ 
    Animal *animal; 
    Cat *cat = new Cat(); 
    Cow *cow = new Cow(); 

    animal = cat; 
    animal->eat(); 
    animal->sleep(); 
    animal->makeNoise(); 

    animal = cow; 
    animal->eat(); 
    animal->sleep(); 
    animal->makeNoise(); 

    return 0; 
} 

Beachten Sie, dass Tier eine abstrakte Klasse ist. Wie kann ich die Zeiger animal, cat und cow richtig löschen?

Als ich delete animal; versuchen erhalte ich die folgende Warnmeldung:

Warnung: Löschen Objekt der abstrakten Klassentyp ‚Animal‘, die nicht-virtuellen Destruktor nicht definiertes Verhalten verursacht hat.

In der anderen Hand, wenn ich zu delete cat; versuchen erhalte ich die folgende Meldung:

Warnung: Löschen von Objekt polymorphen Klassentypen ‚Cat‘, die nicht-virtuellen Destruktor hat möglicherweise nicht definiertes Verhalten verursachen .

+2

Tun Sie wie die Warnung sagt und machen Sie den 'Animal' Destruktor' virtual'. – user0042

Antwort

1

Eine grundlegende C++ - Regel besagt, dass Destruktoren sich von der abgeleiteten Klasse zur Basisklasse hocharbeiten. Wenn ein Cat zerstört wird, wird der Cat Teil zuerst zerstört und der Animal Teil wird danach zerstört.

delete animal; ist undefiniertes Verhalten, weil man, um C++ - Vernichtungsregeln richtig zu folgen, zur Laufzeit wissen muss, welcher abgeleitete Klassenteil vor dem Basisteil Animal zerstört werden sollte. Ein virtual Destruktor macht genau das - er ermöglicht einen dynamischen Versandmechanismus, der sicherstellt, dass die Zerstörung wie vorgesehen funktioniert.

Sie haben keine virtual Destruktor, aber so delete animal macht einfach keinen Sinn. Es gibt keine Möglichkeit, den korrekten Destruktor der abgeleiteten Klasse aufzurufen, und das Zerstören nur des Teils Animal wäre auch nicht genau bedeutungsvolles Verhalten.

Daher macht die C++ - Sprache keine Annahmen darüber, was in einer solchen Situation passieren wird.

Ihr Compiler ist nett genug, Sie davor zu warnen.


Mit delete cat ist die Situation etwas anders. Der statische Typ des cat Zeigers ist Cat*, nicht Animal*, so dass es auch ohne einen dynamischen Dispatch-Mechanismus klar ist, welcher abgeleitete Klasse-Destruktor zuerst aufrufen soll.

Der Compiler warnt Sie immer noch vor diesem Problem, allerdings mit einer anderen Formulierung ("möglicherweise" oder "wird es verursachen"). Ich glaube der Grund ist, dass Cat selbst die Basisklasse für mehr abgeleitete Klassen sein kann, da sie bereits Teil einer Klassenhierarchie mit virtual Funktionen ist.

Es macht anscheinend keine Mühe, eine vollständigere Codeanalyse durchzuführen, um herauszufinden, dass delete cat wirklich harmlos ist.


Um dies zu beheben, machen den Animal destructor virtual. Wenn Sie schon dabei sind, ersetzen Sie Ihre rohen Zeiger durch std::unique_ptr. Sie müssen weiterhin der Destruktorregel virtual für Klassen wie Ihre folgen, aber Sie müssen kein Handbuch delete mehr ausführen.

0

Es ist genau das, worüber der Compiler Sie warnt. Sie müssen Ihre eigenen Destruktoren für jede Klasse definieren und implementieren und sicherstellen, dass sie als virtual für die Vererbung definiert sind.

Verwandte Themen