Frage zu exceptions



  • Folgendes codestück:

    int main()
    {
        resource_allocator("name");    //der destruktor gibt die resource wieder frei...
        mach_dies_und_jenes();         //wirft eine exception, die ich nicht fange
        //wieso wird der destructor nicht aufgerufen?
        return 0;
    };
    

    Das ganze ein wenig anders:

    int main()
    {
        resource_allocator("name");    //der destruktor gibt die resource wieder frei...
        //mach_dies_und_jenes();         //stimmt nicht, sind mehrere funktionen
        try{mach_dies();}catch(...){goto end;};    //kann exc werfen
        try{mach_jenes();}catch(...){goto end;};   //dito
        end:
        return 0;
    };
    

    Das klappt natürlich, ist aber nicht schön (ich halte goto hier für die gängigste Möglichkeit). Gibt es eine bessere Möglichkeit?



  • int main()
    {
    
        try
        {
            mach_dies();
            mach_jenes();
        } 
        catch(...)
        {
            std::cout << "Oops, da hat was nicht geklappt!";
        }
        return 0;
    };
    


  • Ja, stimmt, hatte da wohl tomaten a.d. Augen oder so... Aber, wiese wird der destruktor nicht aufgerufen?



  • Wenn ich mal davon ausgehe dass resource_allocator eine Klasse ist wird hier nur eine temporäre Instanz erzeugt die gleich (nach dieser Codezeile) auch zerstört wird. An dieser Stelle wird auch der Konstruktor aufgerufen.



  • @ness: was fuern Compiler nutzt du?



  • wird die exception nicht so lange "weitergereicht" bis sie gefangen wird?
    also geht es immer weiter bis es bei catch(...) ankommt (das fängt ja alle exceptions... so nun ist es da... und danach kommt ja sofort dein return 0; also hat er gar keine möglichkeit mehr, den destruktor aufzurufen...

    oder irre ich mich jetzt?



  • Da ist kein Objekt... Kein Objekt -> Kein Dekonstruktoraufruf.
    Wenn ein Objekt da wäre würde es beim return 0 statement zerstört werden...



  • Es ist ein Objekt! Ja, i.d. Quellcode oben (den ich a.d. Kopf geschrieben habe) ist ein Fehler, das Objekt ist temporär, deshalb wird es sofort zerstört. Ich benutze den gcc-3.4.3. Wenn keine exc. geworfen wird, wird das Obj. am Ende zerstört. Wenn eine geworfen wird, wird der destruktor nicht aufgerufen.



  • Teste mal das hier:

    #include <iostream>
    
    class test
    {
    	public:
    		test(int a)
    		{
    			std::cout << "Konstruktor" << std::endl;
    		}
    		~test()
    		{
    			std::cout << "Dekonstruktor" << std::endl;
    		}	
    };
    
    void mach_dies()
    {
    	throw(1);
    }
    
    void mach_jenes()
    {
    	throw(1);
    }
    
    int main()
    {
        test(0);
    
    	try
        {
            mach_dies();
            mach_jenes();
        }
        catch(...)
        {
            std::cout << "Exception" << std::endl;
        }
    
    	std::cin.get();
    
        return 0;
    };
    

    Muss die Ausgabe Konstruktor - Dekonstruktor - Exception haben.



  • ness schrieb:

    Es ist ein Objekt! Ja, i.d. Quellcode oben (den ich a.d. Kopf geschrieben habe) ist ein Fehler, das Objekt ist temporär, deshalb wird es sofort zerstört. Ich benutze den gcc-3.4.3. Wenn keine exc. geworfen wird, wird das Obj. am Ende zerstört. Wenn eine geworfen wird, wird der destruktor nicht aufgerufen.

    entweder wird das objekt sofort zerstört, und zwar noch in derselben Zeile,also bevor du diesunddas() aufrufst, oder es handelt sich bei resource_allocator() um eine Funktion, die irgendwo ein Objekt anlegt (vermutlich mit new()), das dann nicht mehr gelöscht wird, weil du den entsprechenden Freigabebefehl (der dann delete aufrufen müsste) nicht aufrufst.

    Im ersten Fall müsste die Ressource eigentlich sofort wieder freigegeben werden, im letzteren wird sie evtl. übrig bleiben.

    Eine andere Möglichkeit ist, dass, während deine Exception irgendwelche Gültigkeitsbereiche verlässt, dort Destruktoren aufgerufen werden. Wenn in den Destruktoren wieder eine Exception geworfen wird (während die andere noch aktiv ist), dann wird sofort terminate aufgerufen und dein Programm verabschiedet sich ganz unentspannt ohne auch nur irgendein Objekt zu zerstören...



  • Aus diesem Grund sollte ein d'tor auch nie werfen..
    Aber wenn man ein throw() macht muesste theoretisch der copy c'tor aufgerufen werden, weil die geworfene Exception durch den copy c'tor(sollte man bei exceptions ausimplementieren und bei anderen Objekten im private Bereich deklarieren um seinen Aufruf zu verhindern) kopiert wird. Da das alte Objekt dann zerstoert wird, muesste der Destruktor einmal fuer das alte und einmal fuer die Kopie aufgerufen werden. Das geschieht beim vc++ 6.0 allerdings nicht was aeusserst seltsam ist.
    🤡 Wir gehen deshalb jetzt von nem internen Exception-Pool oder so aus *g*



  • Hallo,

    wow. sind ja wieder wilde Theorien hier im Umlauf.
    Um mal kurz auf den entscheidenen Punkt zu kommen:

    ob im Originalcode der Dtor aufgerufen wird oder nicht ist Implementations-abhängig. Der Originalcode besitzt *keinen* catch-Block und damit keinen Exceptionhandler. Der C++ Standard lässt es in diesem Fall offen, ob Stack-Unwinding stattfindet oder nicht. Dein Compiler führt in so einem Fall offensichtlich kein Stack-Unwinding durch.

    Ergo: Wann immer man Exceptions verwendet sollte zumindest jede Schnittstelle zur Außenwelt einen catch-All-Handler besitzen. Für ein Programm ist diese Schnittstelle die main-Funktion.

    Aber wenn man ein throw() macht muesste theoretisch der copy c'tor aufgerufen werden, weil die geworfene Exception durch den copy c'tor(sollte man bei exceptions ausimplementieren und bei anderen Objekten im private Bereich deklarieren um seinen Aufruf zu verhindern) kopiert wird

    Theoretisch. Praktisch gehört eine throw-Anweisung zu den Anweisungen in denen der Compiler den Copy-Ctor-Aufruf wegoptimieren darf.
    Sprich hier:
    [cpp]
    throw ExceptionClass();
    {/cpp]
    kann der Compiler das temporäre Objekt auch direkt im Speicher für das Exception-Objekt konstruieren und sich damit eine Kopie sparen.
    Voraussetzung: Der Copy-Ctor ist aufrufbar.



  • kann der Compiler das temporäre Objekt auch direkt im Speicher für das Exception-Objekt konstruieren und sich damit eine Kopie sparen.

    So rein interessehalber. Wie macht er das?



  • PunI$0R schrieb:

    kann der Compiler das temporäre Objekt auch direkt im Speicher für das Exception-Objekt konstruieren und sich damit eine Kopie sparen.

    So rein interessehalber. Wie macht er das?

    Nun, er hat doch alle Infos beisammen, er kann sehen das es ein Parameter für ein throw ist, er weiss das er das Objekt nur dafür braucht, also nimmt der den entsprechenden Speicher und löst dann dafür den Konstruktor für das Exception-Objekt aus. Da ist keine schwarze Magie nötig. 🤡 (Er muß es nicht tun, aber er darf.)



  • oehm wollte eigentlich nur sicher sein das er den c'tor nimmt..
    Das da keine schwarze Magie dahinter steckt war mir schon klar, soweit bin ich schon, das ich weiss das alles irgendwie funktioniert 😉



  • Danke für die klärung, HumeSikkins.


Anmelden zum Antworten