Frage zu Exceptions



  • Hi,

    wenn eine Exception geworfen wurde, und ich diese zum Beispiel in einer Funktion meiner Klasse fange, und mit throw; weiterreiche, damit ich sie nach main() bekomme, weil ich dort alle Exceptions behandeln will, muss ich dann den Speicher den ich in der Klasse mit new reserviert habe, manuell freigeben?

    Meine Klasse hat zwar einen Dekstruktor, der alles macht, aber wird der auch bei einer Exception korrekt ausgeführt?

    Vielen Dank 🙂



  • Beim Hochreichen von Exceptions wird der Stack korrekt aufgeräumt. Das heißt insbesondere, dass für lokale Objekte alle Destruktoren auf dem Weg nach oben aufgerufen werden. Wenn ich deine Frage richtig verstanden habe, solltest du also keine Probleme haben.

    int main() {
    
       try {
          foo();
       }
       catch(...) {
          // ...
       }
       return 0;
    }
    
    void foo() {
    
       MyClass myObj;
       bar();
    }
    
    void bar() {
    
       AnotherClass *anotherObj = new AnotherClass;
       baz();
    }
    
    void baz() {
    
       throw "ERROR!";
    }
    

    Im Beispiel wird myObj korrekt abgeräumt - inklusive Dtor-Aufruf. Das mit new() erzeugte anotherObj wird allerdings ohne weitere Maßnahmen nicht geöscht, da es nicht auf dem Stack liegt.

    Stefan.



  • DStefan schrieb:

    Beim Hochreichen von Exceptions wird der Stack korrekt aufgeräumt. Das heißt insbesondere, dass für lokale Objekte alle Destruktoren auf dem Weg nach oben aufgerufen werden. Wenn ich deine Frage richtig verstanden habe, solltest du also keine Probleme haben.

    int main() {
    
       try {
          foo();
       }
       catch(...) {
          // ...
       }
       return 0;
    }
    
    void foo() {
    
       MyClass myObj;
       bar();
    }
    
    void bar() {
    
       AnotherClass *anotherObj = new AnotherClass;
       baz();
    }
    
    void baz() {
    
       throw "ERROR!";
    }
    

    Im Beispiel wird myObj korrekt abgeräumt - inklusive Dtor-Aufruf. Das mit new() erzeugte anotherObj wird allerdings ohne weitere Maßnahmen nicht geöscht, da es nicht auf dem Stack liegt.

    Stefan.

    So etwas meine ich:

    class MyClass
    {
     private:
      int *x;
    
     public:
      MyClass() 
      { 
       x = new int[1024];  // new[] (!)
       throw "..."; 
      }
    
      ~MyClass() 
      { 
       delete[] x; // delete[] (!) Wird es aufgerufen?
      }
    };
    
    int main()
    {
     try
     {
      MyClass foobar; // Wird der Speicher korrekt freigegeben???
     }
     catch (...)
     {
      // ...
     }
    }
    


  • wenn eine Exception geworfen wurde, und ich diese zum Beispiel in einer Funktion meiner Klasse fange, und mit throw; weiterreiche, damit ich sie nach main() bekomme, weil ich dort alle Exceptions behandeln will

    Du brauchst sie nicht fangen, damit sie nach oben mittels throw weitergereicht wird. Sie propagiert bis zu dem catch, das sie faengt automatisch. Und ja, der Destruktor wird fuer Objekte auf dem Stack aufgerufen. Wie das bei Exceptions ist, die innerhalb eines Konsturktors auftreten, weiss ich nicht. Versuche solche Situationen zu vermeiden.



  • asdasdasd schrieb:

    So etwas meine ich:

    class MyClass
    {
     private:
      int *x;
    
     public:
      MyClass() 
      { 
       x = new int[1024];  // new[] (!)
       throw "..."; 
      }
    

    Wenn in der Funktion, in der eine Exception fliegen kann, etwas mit new angefordert wird, muss die exception gefangen werden und das "etwas" mit delete freigegeben werden. Der Destruktor wird hier nicht aufgerufen, weil der Konstruktor nie ordnungsgemäß verlassen wurde und daher das Objekt nie wirklich existiert hat. Die Member sind zwar alle konstruiert worden, aber nach der Philosophie von C++ beginnt die Lebenszeit eines Objekts erst mit dem (ordnungsgemäßen) Verlassen eines Konstruktors.



  • asdasdasd schrieb:

    So etwas meine ich:

    class MyClass
    {
     private:
      int *x;
    
     public:
      MyClass() 
      { 
       x = new int[1024];  // new[] (!)
       throw "..."; 
      }
    
      ~MyClass() 
      { 
       delete[] x; // delete[] (!) Wird es aufgerufen?
      }
    };
    
    int main()
    {
     try
     {
      MyClass foobar; // Wird der Speicher korrekt freigegeben???
     }
     catch (...)
     {
      // ...
     }
    }
    

    Wie schon gesagt, der Destruktor wird nicht aufgerufen, weil das Objekt nicht erfolgreich "konstruiert" wurde.

    In Deinem Falle würde ich einen Vector als Objekt-Element nehmen. Auch, wenn dann der Konstruktor von MyClass eine Exception schmeißt, werden die Objekt-Elemente ordnungsgemäß wieder zerstört (der Speicher, den der Vektor reserviert hatte, wird wieder freigegeben). Es ist also sehr sinvoll, möglichst nur eine Resource in einem Objekt zu kapseln und ein "größeres" Objekt aus mehreren "kleinen" zusammenzusetzen.

    Sonst sollte man noch die Dreierregel beachten. Man darf nicht vergessen, dass der Compiler versucht, einen Copy-C'tor und einen Copy-Assignment-Operator automatisch hinzuzufügen, falls die Objekt-Elemente das erlauben.

    Gruß,
    SP


Anmelden zum Antworten