2015-07-13 8 views
5

Ich verbrachte mehr als 2 Stunden, um dieses Speicherleck zu finden:Warum verursacht eine Zeichenfolge in einer C++ - Unterklasse Speicherlecks?

class Parent{ 
    ... 
} 

class Child:public Parent{ 
    std::string str; 
    ... 
} 

int main(){ 
    Parent *ptr=new Child(); 
    delete ptr; 
} 

ich es fest, indem Sie die Zeichenfolge in die übergeordneten Klasse zu bewegen. Warum ist dieses Speicherleck aufgetreten? Sollten nicht auch die Kindermitglieder gelöscht werden?

+6

Hat Parent einen virtuellen Destruktor? Sie löschen ein Parent *, wenn der Destruktor nicht virtuell ist, ruft er nicht Child :: ~ Child() auf. – Tas

+4

Hinweis: Wenn "Parent" keinen virtuellen Destruktor hat, ist "delete" Undefined Behavior, was schlimmer ist als ein Speicher Leck. – aschepler

Antwort

11

Dies kann passieren, weil Parent möglicherweise keinen virtuellen Destruktor hat. Da Sie eine Parent* (die Basisklasse) für eine dynamisch zugewiesene abgeleitete Klasse Child erstellen, führt das Löschen der Parent*, die keinen virtuellen Destruktor hat, zu undefiniertem Verhalten, führt jedoch in der Regel dazu, dass die abgeleitete Klasse nicht zerstört wird.

Von Scott Myers - Effective C++ Third Edition:

... wenn wir Basisklasse Zeiger mit einem nicht-virtuellen Destruktor löschen, Ergebnisse nicht definiert sind. In der Regel tritt zur Laufzeit auf, dass der abgeleitete Teil des Objekts niemals zerstört wird. Dies ist eine hervorragende Möglichkeit, Ressourcen zu verlieren, Datenstrukturen zu beschädigen und viel Zeit mit einem Debugger zu verbringen. Also sollte jede Klasse mit virtuellen Funktionen fast sicher einen virtuellen Destruktor haben.

class Parent{ 
}; 

class Child:public Parent{ 
public: 
    Child() : str("Child") {} 
    ~Child() { std::cout << str << std::endl;}; 
    std::string str; 
}; 

int main(){ 
    Parent *ptr=new Child(); 
    delete ptr; // undefined behaviour: deleting a Base* to a Derived object where the Base has no virtual destructor 
} 

Sie können dieses Problem beheben, indem Parent ‚s destructor machen virtual:

class Parent{ 
public: 
    virtual ~Parent() {} // Will call derived classes destructors now as well 
}; 

class Child:public Parent{ 
public: 
    Child() : str("Child") {} 
    ~Child() { std::cout << str << std::endl;}; 
    std::string str; 
}; 

int main(){ 
    Parent *ptr=new Child(); 
    delete ptr; 
    // Child::~Child() has now been called. 
} 

When to use virtual destructors? Sie, welche wahrscheinlich erklärt es besser als ich

bearbeiten: Vielen Dank an die @ascheppler (in den Kommentaren der Frage), Kommentatoren unten und die Antwort auf die verknüpfte Frage, ich habe die Antwort auf die Wette aktualisiert widerlegen, dass dies ein undefiniertes Verhalten ist. In meiner Hast habe ich es nicht erwähnt, und erwähnte nur die typische Verhalten

+3

Tatsächlich ist es nach dem C++ - Standard ein undefiniertes Verhalten, ein abgeleitetes Objekt mit einem Basisklassenzeiger zu löschen, und der Destruktor der Basisklasse ist nicht virtuell. – PaulMcKenzie

+0

@PaulMcKenzie oft "undefined Verhalten" kann konsistent und vorhersehbar sein, auch wenn Sie sich nicht darauf verlassen können. Das Fehlen eines virtuellen Destruktors verursacht sehr wahrscheinlich die in der Frage genannten Symptome. –

+0

@Mark: der zweite Satz der Antwort deutet darauf hin, dass es ein definiertes Verhalten gibt ("nur Parent gelöscht"), das in der Tat falsch ist, wie Paul bemerkt. – MSalters

Verwandte Themen