[Erzeuger] das alte Problem: Wer räumt auf?



  • Sorry, falsch gefragt..

    Was stimmt nichtmit dem Code? Ok er ist nicht vollständig..



  • int main()
    {
        Erzeuger e;
        Objekt *pObjekt = e.erzeugeObjekt("Neu");
    }
    


  • std::auto_ptr



  • hm, also wenn dein Erzeuger den mist in die Welt setzt, sollte das auch wieder beerdigen oder nicht?

    class Erzeuger
    {
        Objekt* erzeugeObjekt(string Name) {
            return new Objekt(Name);
        }
        Objekt* loescheObjekt(Objekt *obj_p) {
            delete obj_p;
        }
    }
    
    int main()
    {
        Erzeuger e;
        Objekt *pObjekt;
        pObjekt = Erzeuger.erzeugeObjekt("Neu");
        Erzeuger.loescheObjekt(pObjekt);
        return 0;
    }
    

    Wichtig für delete ist, dass zur kompilierungszeit klar ist von welchem typ der übergebene Zeiger ist. Wenn du also z.B. zur Laufzeit ein Objekt aus einer library instanzierst, muss die library eine delete-funktion exportieren, da delete mit einem basis-klassen zeiger, hinter dem aber eine abgeleitete Klasse steckt, Mist baut.



  • Hat ihnen dieser Beitrag geholfen? 👍

    [BHN]-Leberknecht schrieb:

    hm, also wenn dein Erzeuger den mist in die Welt setzt, sollte das auch wieder beerdigen oder nicht?

    class Erzeuger
    {
        Objekt* erzeugeObjekt(string Name) {
            return new Objekt(Name);
        }
        Objekt* loescheObjekt(Objekt *obj_p) {
            delete obj_p;
        }
    }
    
    int main()
    {
        Erzeuger e;
        Objekt *pObjekt;
        pObjekt = Erzeuger.erzeugeObjekt("Neu");
        Erzeuger.loescheObjekt(pObjekt);
        return 0;
    }
    

    Wichtig für delete ist, dass zur kompilierungszeit klar ist von welchem typ der übergebene Zeiger ist. Wenn du also z.B. zur Laufzeit ein Objekt aus einer library instanzierst, muss die library eine delete-funktion exportieren, da delete mit einem basis-klassen zeiger, hinter dem aber eine abgeleitete Klasse steckt, Mist baut.



  • [BHN]-Leberknecht schrieb:

    Wichtig für delete ist, dass zur kompilierungszeit klar ist von welchem typ der übergebene Zeiger ist. Wenn du also z.B. zur Laufzeit ein Objekt aus einer library instanzierst, muss die library eine delete-funktion exportieren, da delete mit einem basis-klassen zeiger, hinter dem aber eine abgeleitete Klasse steckt, Mist baut.

    EDIT:

    Du willst also sagen das folgendes "mist" ist? Oder versteh ich da was falsch?

    class base {};
    
    class derived : public base {};
    
    int main() {
      base *obj = new derived;
    
      delete obj;
    }
    


  • Erzeuger.loescheObjekt(pObjekt);
    

    Was ist denn das fürn Mist?
    Dann kann ich auch direkt nen delete aufrufen...
    Also ich würd ein

    std::auto_ptr<Objekt*> erzeugeObjekt(string Name);
    

    daraus machen, wie Arschi gesagt hat.

    Je nach Verwendung auch ein shared_ptr (Gruß an CStoll 😉 ).



  • Generell mußt du unterscheiden zwischen "Erzeuger" und "Besitzer" - und klar dokumentieren, wer dafür zuständig ist (in deinem Beispiel ist main() der Besitzer, muß also auch das Objekt löschen - umgekehrt könnte man die Erzeuger-Klasse auch soweit aufbohren, daß sie den Besitz behält und sich auch irgendwann um die Aufräumarbeiten kümmert).

    Aber die beste Möglichkeit dürften wohl die erwähnten Smart-Pointer sein, um die Aufräumarbeiten zu koordinieren.



  • Cpp-Anfänger schrieb:

    Erzeuger.loescheObjekt(pObjekt);
    

    Was ist denn das fürn Mist?
    Dann kann ich auch direkt nen delete aufrufen...
    Also ich würd ein

    std::auto_ptr<Objekt*> erzeugeObjekt(string Name);
    

    daraus machen, wie Arschi gesagt hat.

    Je nach Verwendung auch ein shared_ptr (Gruß an CStoll 😉 ).

    Das Problem ist, das ein Aufrufer nicht wissen kann, was eine erzeuge()-Funktion alles macht. Vielleicht allokiert er externe Ressourcen und bindet sie an das zurückgegebene Objekt ... und die muß er hinterher wieder freigeben.
    Oder die Daten werden in einer DB abgelegt (und auch dort gelöscht). Oder ...

    Für den hier erwähnten trivialen Fall (in dem tatsächlich ein delete (sei es manuelle oder per auto_ptr) reichte) würde ja wohl auch kaum jemand einen externen Erzeuger bauen, oder ?

    Ich stimme jedenfalls Leberknechts Vorschlag zu: Bietet eine API eine "Quelle" an, sollte sie tunlichst auch eine "Senke" anbieten.
    Noch lieber würde ich allerdings auf "Erzeuger/Vernichter" verzichten...

    Gruß,

    Simon2.



  • Das Problem ist, das ein Aufrufer nicht wissen kann, was eine erzeuge()-Funktion alles macht. Vielleicht allokiert er externe Ressourcen und bindet sie an das zurückgegebene Objekt ... und die muß er hinterher wieder freigeben.
    Oder die Daten werden in einer DB abgelegt (und auch dort gelöscht). Oder ...

    Dafür ist dann ein Destruktor zuständig. Notfalls gebe ich ein Proxyobjekt zurück, wenn ich ressourcen binde, wo ich den Operator-> überlade. Sowas wie Erzeuger.loescheObjekt(pObjekt) brauche ich in C++ ganz sicher nicht. Einfach mal in "Modernes C++ Design" unter Proxy-Objekt nachschauen. Kann man zumindest die Idee "klauen". 😉



  • Artchi schrieb:

    Dafür ist dann ein Destruktor zuständig. Notfalls gebe ich ein Proxyobjekt zurück, wenn ich ressourcen binde, wo ich den Operator-> überlade. Sowas wie Erzeuger.loescheObjekt(pObjekt) brauche ich in C++ ganz sicher nicht. Einfach mal in "Modernes C++ Design" unter Proxy-Objekt nachschauen. Kann man zumindest die Idee "klauen". 😉

    aber manchmal ist eine zigarre bloß eine zigarre.
    daß man passend zur quelle eine senke baut, ist schon ok. da kannste dann drumherum mit proxies spielen. das stört keinen.



  • Artchi schrieb:

    Das Problem ist, das ein Aufrufer nicht wissen kann, was eine erzeuge()-Funktion alles macht. Vielleicht allokiert er externe Ressourcen und bindet sie an das zurückgegebene Objekt ... und die muß er hinterher wieder freigeben.
    Oder die Daten werden in einer DB abgelegt (und auch dort gelöscht). Oder ...

    Dafür ist dann ein Destruktor zuständig. Notfalls gebe ich ein Proxyobjekt zurück, wenn ich ressourcen binde, wo ich den Operator-> überlade. Sowas wie Erzeuger.loescheObjekt(pObjekt) brauche ich in C++ ganz sicher nicht. Einfach mal in "Modernes C++ Design" unter Proxy-Objekt nachschauen. Kann man zumindest die Idee "klauen". 😉

    wie ich schon sage:
    Erste Wahl ist, dass man ohne Erzeuger/Vernichter auskommt - und genau das ist der Fall, wenn man die Funktionalität sinnvoll in die Klasse reinprogrammieren kann (Ctor/DTor).

    WENN man aber nicht darum herumkommt, Erzeuger/Vernichter zu nutzen, sollte man ein Objekt nicht einfach deleten (als Nutzer) bzw. einen geeigneten Vernichter zur Verfügung stellen (als Implementierer).

    Gruß,

    Simon2.



  • Simon2 schrieb:

    wie ich schon sage:
    Erste Wahl ist, dass man ohne Erzeuger/Vernichter auskommt - und genau das ist der Fall, wenn man die Funktionalität sinnvoll in die Klasse reinprogrammieren kann (Ctor/DTor).

    Geht nicht, wenn man ne Abstract Factory und für Polymorphismus wirds auch schwieriger.

    Simon2 schrieb:

    WENN man aber nicht darum herumkommt, Erzeuger/Vernichter zu nutzen, sollte man ein Objekt nicht einfach deleten (als Nutzer) bzw. einen geeigneten Vernichter zur Verfügung stellen (als Implementierer).

    Leuchtet ein


Anmelden zum Antworten