Temporäres Objekt und Exceptions



  • Hallo,

    ich habe in einem Klassen-Wrapper für eine C-Schnittstelle folgendes Konstrukt zur Fehlerbehandlung gefunden.

    class XYZError{
    public:
    	XYZError(const std::string& fct) : m_fct(fct) {}
    	XYZError& operator=(XYZStatus err){
    		if(L_SUCCESS != err){
    			std::ostringstream oss;
    			oss << "Error in " << m_fct << ": ";
    			switch(err){
    				case L_ERR_QUEUE_IS_FULL: oss << "L_ERR_QUEUE_IS_FULL"; break;
    				...
    				default: oss << "unknown error";
    			}
    			throw std::runtime_error(oss.str());
    		}
    		return *this;
    	}
    private:
    	std::string m_fct, m_usr;
    };
    

    Die Klasse wird in den Wrapper-Funktionen nur temporär verwendet:

    void Wrapper::send(...){
    	...
    	XYZError("Transmit")=xyzTransmit(...);
    	if (xyz) throw std::runtime_error("undefined XYZ");
    	...		
    }
    

    Meine Frage ist jetzt, ist die Zeile:
    XYZError("Transmit")=xyzTransmit(...);
    überhaupt zulässig. Es wird ja keine Variable vom Type XYZError angelegt. Die Verarbeitungsreihnfolge:
    - Objekt vom Type XYZError wird erzeugt
    - Funktion xyzTransmit() wird aufgerufen
    - Zuweisungsoperator für Objekt vom Type XYZError wird aufgerufen
    funktioniert zumindest im Debugger korrekt.
    Im Normalfall scheint das ganze zu funktionieren. Nur manchmal, dann aber reproduzierbar, tritt nach dem Werfen einer Exception entweder direkt in XYZError oder in der Zeile danach (siehe Code), im catch-Block oder noch später ein undefinierter Speicherzugriffsfehler auf.

    Das Einfügen einer temporären Variablen behebt "scheinbar" das Problem:

    void Wrapper::send(...){
    	...
    	XYZError error("Transmit");
    	error=xyzTransmit(...);
    	if (xyz) throw std::runtime_error("undefined XYZ");
    	...		
    }
    

    Meine Compiler ist ein CodeWarrior 5.1.1



  • - Objekt vom Type XYZError wird erzeugt
    - Funktion xyzTransmit() wird aufgerufen

    Müsste eigentlich umgekehrt sein? Der Code wird ja von rechts nach links abgearbeitet. Zuerst wird die Funktion aufgerufen, es gibt ein Objekt zurück. XYZError wird instanziert und der =-Operator nimmt das Objekt als Kopie entgegen (vielleicht solltest du hier auch eher eine Reference als Paramter entgegen nehmen?).

    XYZError& operator=(XYZStatus &err){
    


  • @Artchi

    Der Debugger zeigt es in der von mir angegebenen Reihnfolge.
    Ein Vertauschen der Ausführung dieser beiden Punkte sollte theoretisch aber noch keine Probleme bringen.
    Ich frage mich aber, ist vom Standard garantiert, dass das temporäre Objekt vom Type XYZError noch existiert wenn der Zuweisungsoperator ausgeführt wird.

    XYZStatus ist nur ein typedef auf ein int, so dass die Referenz nicht nötig ist.

    DJohn



  • Die temporären Objekte existieren so lange ihr Gültigkeitsbereich nicht verlassen wird. Also in deinem Fall selbst nach der if-Anweisung noch. Außer es wird eine Exception geschmissen, dann wird natürlich im Stack alles abgeräumt.



  • So sollte es auch sein. Aber vielleicht habe ich die Frage auch falsch formuliert.
    Mein bisheriger Code:

    void Wrapper::send(...){
        ...
        XYZError("Transmit")=xyzTransmit(...);
        if (xyz) throw std::runtime_error("undefined XYZ");
        ...       
    }
    

    erzeugt an anderer Stelle Speicherzugriffsfehler, wenn hier eine Exception geworfen wird. Ich dachte das dass Einführen einer zusätzliche Variablen den Fehler behebt (Was beim Testen auch so ist.):

    void Wrapper::send(...){
        ...
        XYZError error("Transmit");
        error=xyzTransmit(...);
        if (xyz) throw std::runtime_error("undefined XYZ");
        ...       
    }
    

    Die Frage ist nur, was ist an dem ersten Codestück fehlerhaft. Sonst habe ich möglicherweise einen anderen bösen Fehler im Programm zu suchen.


Anmelden zum Antworten