Von Konstruktoren, Destruktoren und Suizid-Objekten



  • Hi!

    Ich habe eine Klasse, die in ihrem Konstruktor diverse Handles vom System beantragt und anderweitige Ressourcen belegt. Jetzt kann es passieren, dass beim allokieren dieser Ressourcen es zu einem Fehler kommt und damit die gesamte Instanz der Klasse unbrauchbar wird. Ich muss also alle belegten Ressourcen wieder frei geben und den Konstruktor abbrechen (über throw Exception).

    Nun habe ich allerdings das Problem, dass der Aufrufende Code ein Objekt hat, dass effektiv eine Leiche ist, da es alle Ressourcen wieder frei gegeben hat.

    Stellt sich für mich die Frage, ob man folgendes machen sollte / darf / kann / muss:

    CMyClass::CMyClass(void)
    {
       this->GetLotsOfSystemRessources();
       if (this->m_bFatalErrorFlag)
       {
          this->ReleaseLotsOfSystemRessources();
          delete this;
          throw EFatalException(EFatalException::ERR_ALLOC_SYS_RES)
       }
    }
    

    Wenn ich soetwas vermeiden sollte, wie kann ich sonst sicher gehen, dass mein Objekt und seine Kumpels nicht irgendwo "Day of Living Dead X" drehen? Oder ist einfach der aufrufende Code nach abfangen des Fehlers für das freigeben des Objektes zuständig?

    Grüße,
    Daniel



  • schonmal von RAII gehört? (bzw. RRID)?

    http://en.wikipedia.org/wiki/RAII
    ausführlich: http://www.hackcraft.net/raii/

    delete this im konstruktor ist übrigens *nie* notwendig, das geschieht automatisch (ohne aufruf des destruktors, da ja noch gar kein fertiges objekt existiert), soll heißen der speicher wird (sofern normal angefordert) automatisch freigegeben. nur um deine resourcen musst du dich selbst kümmern (also filehandles, sockets, whatever) - und zwar mit RAII



  • Ok, cool. Dann hat das Kind ja auch einen Namen. Das habe ich schon oft benutzt weil es einfach und intuitiv ist. Aber so ein schöner Fachbegriff... 😉

    Also, um das Konzept kurz zu erläutern:
    Man sorgt dafür, dass die Allokation von Ressourcen bei der Initialisierung einer Klasse erfolgt und die Freigabe dann automatisch im Destruktor vorgenommen wird.

    In deinem Fall ungefähr so:

    class CMyClass
    {
    public:
    	// Konstruktor: Allokation und Fehlerüberprüfung
    	CMyClass()
    	{
    	   GetLotsOfSystemRessources();
    
    	   if (this->m_bFatalErrorFlag)
    	      throw EFatalException(EFatalException::ERR_ALLOC_SYS_RES);
    	} 
    
    	// Destruktor: Ressourcenfreigabe
    	~CMyClass()
    	{
    	   ReleaseLotsOfSystemRessources();
    	} 
    
    	// Optional je nach Anwendung zusätzlich: Vorzeitige Ressourcenfreigabe.
    	// Achtung: erfordert dann aber bei jeder Ressourcenbenutzung
    	// eine Gültigkeitsprüfung!
    	close()
    	{
    	   ReleaseLotsOfSystemRessources();
    	} 
    
    	// Vielleicht auch sowas
    	reopen()
    	{
    	   ReleaseLotsOfSystemRessources();
    	   GetLotsOfSystemRessources();
    	} 
    };
    


  • Das wie du es da machst, wird nicht funktionieren, weil der Destruktor nur für vollständig konstruierte Objekte, für die der Konstruktor also keine Exception geworfen hat aufgerufen wird.

    EDIT: Stattdessen könnte man für jede einzelne Ressource ein einzelnes Teilobjekt verwenden. Denn für teilweise konstruierte Objekte wird der Destruktor für alle schon konstruierten Teilobjekte aufgerufen. Das heißt, wenn eine Allokation (oder was auch immer) fehlschlägt, kann dann eine Excpetion geworfen werden, so dass automatisch alle schon erzeugten Subobjekte sauber zerstört werden.

    Felix



  • sicher funktioniert das so, wenn CMyClass in dem beispiel eben nur für die ressource "GetLotsOfSystemRessources" zuständig ist.
    die exception darf natürlich nur geworfen werden, wenn GetLotsOfSystemRessources fehlschlägt, d.h. wenn GetLotsOfSystemRessources *keine* ressource zurückgibt - denn dann muss auch nichts wieder freigegeben werden. CMyClass darf dann klarerweise auch *nur* für diese ressource zuständig sein.


Anmelden zum Antworten