webentwicklung-frage-antwort-db.com.de

Wird ein Destruktor aufgerufen, wenn ein Objekt den Gültigkeitsbereich verlässt?

Beispielsweise:

int main() {
    Foo *leedle = new Foo();

    return 0;
}

class Foo {
private:
    somePointer* bar;

public:
    Foo();
    ~Foo();
};

Foo::~Foo() {
    delete bar;
}

Wird der Destruktor implizit vom Compiler aufgerufen, oder liegt ein Speicherverlust vor?

Ich bin neu im dynamischen Speicher. Wenn dies also kein brauchbarer Testfall ist, tut es mir leid.

35
Tux

Ja, automatische Variablen werden am Ende des umschließenden Codeblocks zerstört. Aber lies weiter.

Ihr Fragentitel fragt, ob ein Destruktor aufgerufen wird, wenn die Variable den Gültigkeitsbereich verlässt. Vermutlich wollten Sie fragen:

wird Foos Destruktor am Ende von main () aufgerufen?

In Anbetracht des von Ihnen angegebenen Codes lautet die Antwort auf diese Frage nein, da das Foo-Objekt eine dynamische Speicherdauer hat, wie wir in Kürze sehen werden.

Beachten Sie hier, was die automatische Variable ist:

Foo* leedle = new Foo();

Hier ist leedle die automatische Variable, die zerstört wird. leedle ist nur ein Zeiger. Die Sache, auf die leedle zeigt, hat keine automatische Speicherdauer und wird nicht zerstört. Also, wenn Sie dies tun:

void DoIt()
{
  Foo* leedle = new leedle;
}

Sie verlieren den durch new leedle Zugewiesenen Speicher.


Sie müssendelete alles, was mit new belegt wurde:

void DoIt()
{
  Foo* leedle = new leedle;
  delete leedle;
}

Dies wird durch die Verwendung intelligenter Zeiger viel einfacher und robuster. In C++ 03:

void DoIt()
{
  std::auto_ptr <Foo> leedle (new Foo);
}

Oder in C++ 11:

void DoIt()
{
  std::unique_ptr <Foo> leedle = std::make_unique <Foo> ();
}

Intelligente Zeiger werden wie oben beschrieben als automatische Variablen verwendet. Wenn sie den Gültigkeitsbereich verlassen und zerstört werden, wird automatisch (im Destruktor) delete auf das Objekt verwiesen, auf das verwiesen wird. In beiden oben genannten Fällen liegt also kein Speicherverlust vor.


Lassen Sie uns versuchen, ein bisschen Sprache zu klären. In C++ haben Variablen eine Speicherdauer. In C++ 03 gibt es 3 Speicherdauern:

1: automatisch: Eine Variable mit automatischer Speicherdauer wird am Ende des umschließenden Codeblocks zerstört.

Erwägen:

void Foo()
{
  bool b = true;
  {
    int n = 42;
  } // LINE 1
  double d = 3.14;
} // LINE 2

In diesem Beispiel haben alle Variablen eine automatische Speicherdauer. Sowohl b als auch d werden in Zeile 2 zerstört. n werden in Zeile 1 zerstört.

2: statisch: Eine Variable mit statischer Speicherdauer wird vor Programmbeginn zugewiesen und bei Programmende gelöscht.

3: dynamisch: Eine Variable mit dynamischer Speicherdauer wird zugewiesen, wenn Sie sie mit dynamischen Speicherzuweisungsfunktionen zuweisen (z. B. new), und wird zerstört, wenn Sie sie mit dynamischem Speicher löschen Zuweisungsfunktionen (zB delete).

In meinem ursprünglichen Beispiel oben:

void DoIt()
{
  Foo* leedle = new leedle;
}

leedle ist eine Variable mit automatischer Speicherdauer und wird am Ende der Klammer zerstört. Das Ding, auf das leedle zeigt, hat eine dynamische Speicherdauer und wird im obigen Code nicht zerstört. Sie müssen delete aufrufen, um die Zuordnung aufzuheben.

C++ 11 fügt außerdem eine vierte Speicherdauer hinzu:

4: thread: Variablen mit Thread-Speicherdauer werden zu Beginn des Threads zugewiesen und zu Ende des Threads freigegeben.

89
John Dibling

Ja, wenn ein Objekt den Gültigkeitsbereich verlässt, wird der Destruktor aufgerufen. ABER Nein, der Destruktor wird in diesem Fall nicht aufgerufen, da Sie nur einen Zeiger haben im Gültigkeitsbereich haben Der Zeiger hat keinen bestimmten Destruktor, daher wird der Destruktor von Foo nicht indirekt aufgerufen.

Dieses Beispiel ist die Anwendungsdomäne von intelligenten Zeigern wie std::unique_ptr und std::shared_ptr. Dies sind tatsächliche Klassen, die im Gegensatz zu rohen Zeigern have einen Destruktor darstellen, der (bedingt) delete für das Objekt aufruft, auf das verwiesen wird.

Übrigens, der Destruktor von Foo löscht bar, bur bar wurde noch nie initialisiert oder einer Adresse zugewiesen, die auf ein tatsächliches Objekt zeigt, sodass der Löschaufruf wahrscheinlich ein undefiniertes Verhalten ergibt ein Unfall.

7
Arne Mertz

es würde tatsächlich ein Speicherleck geben. Der Destruktor für das Objekt, das außerhalb des Gültigkeitsbereichs liegt (das Foo *), wird aufgerufen, der Destruktor für das Objekt, auf das verwiesen wird (das Foo, das Sie zugewiesen haben), jedoch nicht.

Technisch gesehen ist dies kein Speicherverlust, da Sie bis zu dem Zeitpunkt, an dem die Anwendung nicht beendet wird, auf jede zugewiesene Variable zugreifen können. In diesem Zusammenhang zitiere ich Alexandrescu (aus Modern C++, dem Kapitel über Singletons).

Speicherverluste treten auf, wenn Sie akkumulierende Daten zuordnen und alle Verweise darauf verlieren. Dies ist hier nicht der Fall: Es sammelt sich nichts an, und wir behalten das Wissen über den zugewiesenen Speicher bis zum Ende der Anwendung. Außerdem alles modern

Dies bedeutet natürlich nicht, dass Sie delete nicht aufrufen sollten, da dies eine äußerst schlechte (und gefährliche) Praxis wäre.

2
Stefano Falasca

Beachten Sie zunächst, dass der Code nicht kompiliert werden kann. new gibt einen Zeiger auf ein Objekt zurück, das auf dem Heap zugeordnet ist. Du brauchst:

int main() {
    Foo *leedle = new Foo();
    return 0;
}

Nun, da new dem Objekt einen dynamischen Speicher anstatt einen automatischen zuweist, verlässt es am Ende der Funktion nicht den Gültigkeitsbereich. Daher wird es auch nicht gelöscht, und Sie haben Speicher verloren.

1
Joni