deinitialisierung



  • Ich hab finally auch erst vermisst, als ich von Delphi kam, aber man gewöhnt sich IMHO schnell an die C++-Version.
    Mittlerweile würde ich sogar (mal wieder...) sagen, dass delete/delete[]/FreeSomeResource() etc. *nichts* in Code zu suchen hat, der nicht als *einzige* Aufgabe hat, Resource XYZ zu verwalten. Damit kann man sich bei den meisten Klassen auch ersparen, einen Destruktor, Zuweisungsoperator und Kopierkonstruktor eigenhändig zu schreiben.



  • Normalerweise löst man das, indem man Resourcen in Klassen kapselt, deren Destruktor die Aufräumarbeit übernimmt.

    Da stimme ich dir grundsätzlich zu, doch was ist, wenn dies nicht möglich ist:

    [cpp]
    class K2D_Hardware{
    private:
    // reference - flag
    static bool bInitialized;

    // WinAPI
    HINSTANCE hInst;
    HWND hWnd;

    // Direct3D
    IDirect3D9 *pD3D;
    IDirect3D9Device *D3DDev;
    IDirect3D9VertexBuffer9 *pD3DVBuf;

    // DirectInput
    IDirectInput8 *pDI;
    IDirectInputDevice8 *pDIKeyb,*pDIMouse;

    // DirectSound
    IDirectSound8 *pDS;
    IDirectSoundBuffer8 *pDSPrimBuf;

    // usw...
    public:
    K2D_Hardware(HWND hWnd);
    };

    bool K2D_Hardware::bInitialized = false;

    K2D_Hardware(HWND hWND){
    // make sure there are not multiple instances of this class
    if(K2D_Hardware::bInitialized) throw K2D_Exception(KD2_Exception::MultipleInit,"K2D_Hardware");

    // copy the window - handle and extract the instance - handle
    this->hWnd = hWnd;
    this->hInst = GetWindowLong(hWnd,GWL_HINSTANCE);

    // init the Direct3D - Interfaces by zero
    this->pD3D = 0x00;
    this->pD3DDev = 0x00;
    this->pD3DVBuf = 0x00;

    // init the DirectInput - Interfaces by zero
    this->pDI = 0x00;
    this->pDIKeyb = this->pDIMouse = 0x00;

    // init the DirectSound - Interfaces by zero
    this->pDS = 0x00;
    this->pDSPrimBuf = 0x00;

    // set the initialized - flag to true to avoid multiple initialisation
    K2D_Initialized = true;

    // try to create a IDirect3D - Instance
    if(!(this->pD3D = Direct3DCreate9(D3D_SDK_VERSION))) throw K2D_Exception(K2D_Exception::FailedInit,"IDirect3D9");

    // try to create a IDirect3DDevice - Instance
    if(this->pD3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,this->hWnd,D3DCREATE_HARDWARE_VERTEXPROCESSING,&this->D3DProp,&this->pD3DDev) != D3D_OK){
    this->pD3D->Release();
    throw K2D_Exception(K2D_Exception::FailedInit,"IDirect3DDevice9");
    }

    // try to create a IDirect3DVertexBuffer - Instance
    if(this->pD3DDev->CreateVertexBuffer9(32*sizeof(float),D3DUSAGE_WRITEONLY,D3DFVF_XYZ|D3DFVF_NORMALS|D3DFVF_TEX2,D3DPOOL_MANAGED,&this->pD3DVBuf,0x00) != D3D_OK){
    this->pD3DDev->Release();
    this->pD3D->Release();
    throw K2D_Exception(K2D_Exception::FailedInit,"IDirect3DVertexBuffer9");
    }

    // try to create a DirectInput - Instance
    if(DirectInput8Create(this->hInst,DIRECTINPUT_VERSION,IID_IDirectInput8,&this->pDI,0x00) != DI_OK){
    this->pD3DVBuf->Release();
    this->pD3DDev->Release();
    this->pD3D->Release();
    throw K2D_Exception(K2D_Exception::FailedInit,"IDirectInput8");
    }
    // usw...
    }

    Wenn eine Excepion in einem Konstruktor ausgelöst wird, wird das Object niemals instanziert und folgedessen auch niemals einen Destruktor aufgerufen, der die DirectX - Instance wieder entfernen würde, wenn irgendwas schiefgeht -> Und glaubt mir, es kann eine Menge schiefgehen!!! 😉

    Wie würdet ihr denn dieses Problem angehen ?

    P.S.
    Dies ist nur Beispiel - Code den ich schnell geschriben habe um das Problem zu verabschaulichen! Ich habe den Code niemals compiliert und er kann daher voller Fehler sein. Ach ja und noch etwas: Ich benutze den this Operator und basta!!! 🙂

    Einen freundlichen Gruss an alle
    Ishildur

    (Sorry hab zu lange gebraucht, um diesen Beitrag zu schreiben weshalb vermutlich die session abgelaufen ist. Daher unregistriert...



  • Wie würdet ihr denn dieses Problem angehen ?

    Jede dieser ganzen Resourcen in eine eigene Handle-Klasse packen. Befass dich mal mit Exceptionsicherheit (lies den Stroustrup, oder "Exceptional C++", oder stöber ein wenig auf www.gotw.ca herum)

    Ach ja und noch etwas: Ich benutze den this Operator und basta!!! 🙂

    Es gibt keinen this Operator :p



  • Jede dieser ganzen Resourcen in eine eigene Handle-Klasse packen

    Also meiner Meinung nach sollte eine Klasse nur dann verwendet werden, wenn auch mehrere Instanzen davon gemacht werden. Die Hardwareklasse ist hier eine Ausnahme, da natürlich nicht mehrere Grafikkarten vorhanden sind und dient auschliesslich zum Verbergen der API. Doch Normalerweise teilt man doch eine Klasse nur dann auf, wenn mehrere Object davon instanziert werden ?

    Gruss Ishlildur



  • Warum sollte das so sein? Du hast ein Problem, Handle-Klassen lösen dir das ... reicht das nicht? Naja, du kannst es natürlich auch kompliziert machen, wenn es mit deinem Weltbild kollidiert 😉



  • a) für die Interface-Zeiger Smart Pointer, z.B. CComPtr/CComQIPtr, oder _com_ptr_t, oder das lokale äquivalent (notfalls selber schreiben). Macht viel mehr Spaß so

    b) für die anderen Sachen kann man sich ja ein schönes template schreiben (oder wiederverwenden) -
    Allerdings muß man schon sagen, daß das wrappen von Handles eine Wissenschaft für sich ist. Probleme gibt es meistens an den Übergangsstellen unwrapped/wrapped

    c) Wenn du exception-freien copde schreibst (was ja möglich ist), kannst du dir mit folgendem behelfen:

    bool Foo()
    {
       thingie1 = NULL;
       thingie2 = NULL;
       do {  // while 0 - for bail out
         thingie1 = acquireThingie1();
         if (thingie1 == NULL) break;
    
         thingie2 = acquireThingie2();
         if (thingie2 == NULL) break;
    
         return true;
    
       } while(0);
    
       if (thingie1 != NULL) {
         releaseThingie1(thingie1);
         thingie1 = NULL; // wenn member/outside of func scope
       }
    
       if (thingie2 != NULL) {
         releaseThingie2(thingie1);
         thingie2 = NULL; // wenn member/outside of func scope
       }
    
       return false;
    }
    

    separiert das Cleanup vom eigentlichen Programmfluß, ist aber mit Vorsicht zu genießen.

    /edit: das heißt nur mehr [cpp ] [/cpp ]



  • Also den Code finde ich mies...

    ein

    {
      SecureThing1 a(acquireThing1());
      SecureThing2 b(acquireThing2());
    }
    

    finde ich da viel schöner - und das aufräumen fällt gänzlich aus dem wichtigen code raus.

    wo soll das problem liegen? erklär mir das mal genauer - ich bin bisher damit recht gut gefahren...



  • @Basar
    Hmm, du meinst also etwas folgendermassen?

    class DirectInput{
    private:
     IDirectInput8 *pDI;
    
    public:
     DirectInput(HINSTANCE hInst);
    };
    
    DirectInput::DirectInput(HINSTANCE hInst){
     if(DirectInput8Create(hInst,DIRECTINPUT_VERSION,IID_IDirectInput8,&this->pDI,0x00) != DI_OK) throw Exception();
    }
    
    class Hardware{
    private:
     DirectInput *pDI;
    
    public:
     Hardware(HINSTANCE hInst);
    };
    
    Hardware::Hardware(HINSTANCE hInst){
     try{
      this->pDI = new DirectInput(hInst);
     }
     catch(Exception &e)
      throw e;
     }
    }
    

    Wenn etwas in DirectInput schiefgeht, wird Sie gar nie erzeugt und muss somit auch niemals deinitialisiert werden...
    Soweit so gut, und trotzdem kann ich mich mit dem Gedanken nicht anfreunden, für jede DirectX Klasse eine Wrapperklasse zu schreiben -> Es gibt verdammt viele Klassen in DirectX. Passt einfach nicht in mein Weltbild, was soll ich machen ? 😉

    Gruss Ishildur



  • Wo ist das Problem?

    Du schreibst zwar einmal eine Menge wrapper - hast dafür aber nie mehr Fehlerbehandlungscode in deinem code...

    erklär mal was dich daran stört (ausser der schreibaufwand)

    ich kenne directX nicht, aber bei der winapi verwende ich auch für alles wrapper - denn es ist einfach besser wenn eine exception fliegt und alle resourcen automatisch gelöscht werden, als wenn ich jede funktion auf fehler prüfen muss und dann immer an 1000 verschiedenen stellen 10000 resourcen freigeben muss...



  • btw:
    statt

    Hardware::Hardware(HINSTANCE hInst){
     try{
      this->pDI = new DirectInput(hInst);
     }
     catch(Exception &e)
      throw e;
     }
    }
    

    würde ich einfach

    Hardware::Hardware(HINSTANCE hInst) : pDI(new DirectInput(hInst))
    {}
    

    schreiben...

    ich glaube du kennst dich mit c++ noch nicht so wirklich aus... -> zB initialisierungsliste

    und ein Dtor fehlt dir auch 🙄



  • @Ishildur: COM Smart Pointer. Wirklich.

    @SoM:
    Erfüllt seinen Zweck solang man es nicht übertreibt.

    Wie sieht der Destruktor eines HKEY-Wrappers aus? Und der Copy-Constructor?

    Man hat eigentlich nur zwei Varianten:
    a) man isoliert die Anwendung vollständig von dem gewrappten Handle.
    Hat den Nachteil, das man alle mit dem Handle möglichen Operationen wrappen muß - das ist besonders schrecklich, wenn das Handle als Bridge zwischen versachiedenen Entities agieren kann.

    b) Man erlaubt der Anwendung den Zugriff auf das gewrappte Handle.
    Hier erhält man mehr Freiheiten für weniger Abstraktion - und es kann schnell passieren daß man das Handle direkt oder indirekt dem Wrapper unter dem Hintern wegzieht.

    Welcher Weg überhaupt gangbar ist, hängt so ziemlich von der vorhandenen API ab - und läßt sich damit für verschiedene Handles nicht einheitlich lösen.

    Wenn du's ganz modern willst, nimm halt ScopeGuards -
    http://www.cuj.com/documents/s=8000/cujcexp1812alexandr/alexandr.htm

    verwende ich an den Stellen, wo die break-varainte zu unübersichtlich wird, oder ich mit exceptions rechnen muß

    (zu obiger Frage: die flexible Lösung sind zwei Klassen - eine "HKEY-Data" - Klasse mit Referenzzähler, und ein Smart Pointer, der die Registry-Zugriffe implementiert. Das geht wunderbar, und lohnt sich für die Registry - ist aber für andere Handles wiederrum nicht gangbar.)


Anmelden zum Antworten