Frage

class someclass {};

class base
{
    int a;
    int *pint;
    someclass objsomeclass;
    someclass* psomeclass;
public:
    base()
    {
        objsomeclass = someclass();
        psomeclass = new someclass();
        pint = new int(); 
        throw "constructor failed";
        a = 43;
    }
}

int main()
{
    base temp();
}

In dem obigen Code, wirft der Konstruktor. Welche werden Objekte ausgelaufen ist, und wie können die Speicherlecks vermieden werden?

int main()
{
    base *temp = new base();
}

Wie wäre es in dem obigen Code? Wie lassen sich die Speicherlecks vermieden werden, nachdem der Konstruktor wirft?

War es hilfreich?

Lösung

Ja, es wird ein Speicherleck. Wenn der Konstruktor wirft, wird kein Destruktor aufgerufen werden (in diesem Fall zeigen Sie keine destructor, die dynamisch zugewiesenen Objekte befreit, sondern lässt vermuten, man Sie hatte).

Dies ist ein wichtiger Grund, intelligente Zeiger zu verwenden - da die Smart poitners vollwertiges Objekte sind, werden sie Destruktoren während der Ausnahme des Stapels Abwickler und haben die Möglichkeit genannt bekommen den Speicher frei

.

Wenn Sie so etwas wie Boost-der scoped_ptr <> Vorlage verwenden, könnte Ihre Klasse aussehen wie:

class base{
    int a;
    scoped_ptr<int> pint;
    someclass objsomeclass;
    scoped_ptr<someclass> psomeclass;
    base() : 
       pint( new int),
       objsomeclass( someclass()),
       psomeclass( new someclass())

    {
        throw "constructor failed";
        a = 43;
    }
}

Und Sie würden keine Speicherlecks haben (und die Standard-dtor würde auch die dynamische Speicherzuweisung aufzuräumen).


Um es zusammenzufassen (und hoffentlich auch Antworten auf die Frage nach der

base* temp = new base();

Anweisung):

Wenn eine Ausnahme in einem Konstruktor geworfen wird, gibt es einige Dinge, die Sie zur Kenntnis, in Bezug auf den richtig Umgang mit Ressourcenzuweisungen nehmen sollen, die in dem abgebrochenen Bau des Objekts stattgefunden haben können:

  1. der destructor für das Objekt aufgebaut ist, wird nicht aufgerufen werden.
  2. Destruktoren für Mitgliedsobjekte in die Klasse des Objekts enthalten sind, werden aufgerufen werden
  3. der Speicher für das Objekt, das gebaut wurde befreit werden.

Das bedeutet, dass, wenn Ihr Objekt Ressourcen besitzt, haben Sie zwei Methoden zur Verfügung, um diese Ressourcen zu bereinigen, die bereits erworben haben könnten, wenn der Konstruktor wirft:

  1. die Ausnahme abfangen, lassen Sie die Ressourcen, dann erneut auslösen. Dies kann schwierig sein, richtig zu bekommen und ein Wartungsproblem werden kann.
  2. verwenden, um Objekte, die Ressourcen Lebensdauer (RAH) und verwenden diese Objekte als Mitglieder zu verwalten. Wenn der Konstruktor für das Objekt eine Ausnahme auslöst, werden die Mitgliedsobjekte desctructors genannt haben und die Möglichkeit haben, die Ressource, deren Leben zu befreien, für die sie verantwortlich sind.

Andere Tipps

Die beiden neuen Wille geleckt werden.

Weisen Sie die Adresse der Heap-Objekte erstellt auf Namen Smart-Pointer, so dass es in dem Smart-Pointer destructor gelöscht werden, die rufen Sie erhalten, wenn die Ausnahme ausgelöst wird - ( RAH ).

class base {
    int a;
    boost::shared_ptr<int> pint;
    someclass objsomeclass;
    boost::shared_ptr<someclass> psomeclass;

    base() :
        objsomeclass( someclass() ),
        boost::shared_ptr<someclass> psomeclass( new someclass() ),
        boost::shared_ptr<int> pint( new int() )
    {
        throw "constructor failed";
        a = 43;
    }
};

Jetzt psomeclass & Pint Destruktoren wird aufgerufen, wenn der Stapel Abwickler, wenn die Ausnahme im Konstruktor geworfen wird, und die Destruktoren den zugewiesenen Speicher freigeben.

int main(){
    base *temp = new base();
}

Für gewöhnliche Speicherzuweisung mit (nicht-plcaement) neu, Speicher durch den Bediener zugewiesen neuen freigegeben wird automatisch, wenn der Konstruktor eine Ausnahme auslöst. new'ly zugeordnet, in anderen Fällen nicht in Bezug auf, warum die einzelnen Mitglieder die Mühe zu befreien (in Reaktion auf die Kommentare zu Mike B Antwort), die automatische Befreiung gilt nur, wenn eine Ausnahme in einem Konstruktor eines Objekts geworfen wird. Auch der Speicher, der freigegeben wird, ist die für die Objektelemente zugeordnet sind, nicht Speicher, den Sie zugewiesen haben könnte sagen, innerhalb des Konstruktors. dh es würde den Speicher frei für die Membervariablen a Pint objsomeclass und psomeclass , aber nicht die Speicher zugewiesen von new Someclass () und new int () .

Ich glaube, dass die Top-Antwort falsch ist und nach wie vor ein Speicherleck würde. Der Destruktor für die Klassenmitglieder werden nicht aufgerufen werden, wenn der Konstruktor eine Ausnahme auslöst (weil es nie seine Initialisierung abgeschlossen, und vielleicht haben einige Mitglieder erreicht nie ihre Konstruktoraufrufe). Ihre Destruktoren werden nur während der Klasse des Destruktoraufrufs genannt. Das macht nur Sinn.

Dieses einfache Programm zeigt es.

#include <stdio.h>


class A
{
    int x;

public:
    A(int x) : x(x) { printf("A constructor [%d]\n", x); }
    ~A() { printf("A destructor [%d]\n", x); }
};


class B
{
    A a1;
    A a2;

public:
    B()
    :   a1(3),
        a2(5)
    {
        printf("B constructor\n");
        throw "failed";
    }
    ~B() { printf("B destructor\n"); }
};


int main()
{
    B b;

    return 0;
}

Mit der folgenden Ausgabe (mit g ++ 4.5.2):

A constructor [3]
A constructor [5]
B constructor
terminate called after throwing an instance of 'char const*'
Aborted

Wenn Ihr Konstruktor partway versagt, dann ist es in Ihrer Verantwortung, damit umzugehen. Schlimmer noch, wird die Ausnahme kann von der Basisklasse Konstruktor geworfen! Die Art und Weise mit diesen Fällen umgehen ist durch einen „Funktion Try-Block“ verwendet wird (aber auch dann müssen Sie sorgfältig die Zerstörung des teilweise initialisiert Objekt-Code).

Der richtige Ansatz, um Ihr Problem wäre dann so etwas wie dies:

#include <stdio.h>


class A
{
    int x;

public:
    A(int x) : x(x) { printf("A constructor [%d]\n", x); }
    ~A() { printf("A destructor [%d]\n", x); }
};


class B
{
    A * a1;
    A * a2;

public:
    B()
    try  // <--- Notice this change
    :   a1(NULL),
        a2(NULL)
    {
        printf("B constructor\n");
        a1 = new A(3);
        throw "fail";
        a2 = new A(5);
    }
    catch ( ... ) {   // <--- Notice this change
        printf("B Cleanup\n");
        delete a2;  // It's ok if it's NULL.
        delete a1;  // It's ok if it's NULL.
    }

    ~B() { printf("B destructor\n"); }
};


int main()
{
    B b;

    return 0;
}

Wenn Sie es starten Sie die erwartete Ausgabe, wo nur die zugewiesenen Objekte zerstört werden und befreit.

B constructor
A constructor [3]
B Cleanup
A destructor [3]
terminate called after throwing an instance of 'char const*'
Aborted

Sie können es immer noch mit intelligenten gemeinsamen Zeiger arbeiten, wenn Sie mit zusätzlichen Kopieren zu wollen. Schreiben Sie einen Konstruktor wie folgt aus:

class C
{
    std::shared_ptr<someclass> a1;
    std::shared_ptr<someclass> a2;

public:
    C()
    {
        std::shared_ptr<someclass> new_a1(new someclass());
        std::shared_ptr<someclass> new_a2(new someclass());

        // You will reach here only if both allocations succeeded. Exception will free them both since they were allocated as automatic variables on the stack.
        a1 = new_a1;
        a2 = new_a2;
    }
}

Viel Glück, Tzvi.

Wenn Sie in einem Konstruktor werfen, sollten Sie alles aufzuräumen, die kamen, bevor der Anruf zu werfen. Wenn Sie die Vererbung verwenden oder in einem destructor werfen, sollten Sie wirklich nicht sein. Das Verhalten ist seltsam (nicht der Standard zur Hand hat, aber es könnte nicht definiert werden?).

Ja, dass Code Leck-Speicher. Speicherblöcke zugeordnet mit „neuen“ werden nicht freigegeben, wenn eine Ausnahme ausgelöst wird. Dies ist Teil der Motivation hinter RAII .

den Speicherverlust zu vermeiden, versuchen, etwas wie folgt aus:

psomeclass = NULL;
pint = NULL;
/* So on for any pointers you allocate */

try {
    objsomeclass = someclass();
    psomeclass = new someclass();
    pint = new int(); 
    throw "constructor failed";
    a = 43;
 }
 catch (...)
 {
     delete psomeclass;
     delete pint;
     throw;
 }

Alles, was man „neu“ muss gelöscht werden, oder Sie einen Speicherverlust verursachen. Also diese beiden Zeilen:

psomeclass = new someclass();
pint = new int(); 

Wird Speicherlecks verursachen, da Sie tun müssen:

delete pint;
delete psomeclass;

In einem finally zu vermeiden, blockieren sie durchgesickert.

Auch diese Zeile:

base temp = base();

ist nicht erforderlich. Sie brauchen nur zu tun:

base temp;

Das Hinzufügen des "= base ()" ist nicht erforderlich.

Sie müssen psomeclass ... Es ist nicht notwendig, um aufzuräumen die ganze Zahl löschen ...

RWendi

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top