Objekte instanzieren



  • Hallo Leute
    Wie kann ich verhindern, dass ein Objekt instanziert werden kann ??

    z.B.

    class Object{
    private:
     IDirect3DDevice9 *pDev; // Dieser Pointer muss gekapselt werden
    };
    
    bool createObject(Object **ppObj){
     *ppObj = new Object;
     (*ppObj)->pDev = // irgendwoher, aber der Benutzer der API darf davon nichts wissen, es darf also nicht in einem Konstruktor übergeben werden
     return true;
    }
    
    Object obj; // soll einen kompilerfehler verursachen
    Object *pObj = new Objec; // solle einen kompilerfehler verursachen
    
    // nur so solls gehen:
    Object *ppObj = 0x00;
    CreateObject(ppObj);
    

    Das ist natürlich extrem vereinfacht, doch ich hoffe, dass die Motivation daraus ersichtlich wird...

    Gruss Ishildur



  • du könntest den Konstruktor privat machen und die Factoryfunktion zum friend erklären.



  • Ich verstehe den Sinn nicht: Irgend jemand muß wissen, wie dieses IDirect3DDevice9 erzeugt/ermittelt wird. Warum nicht Object (im Konstruktor)? Stattdessen hast du eine Funktion, die ein Object mit new() erzeugt und dann dessen privaten Pointer setzt? Finde ich pervers. Und "bool createObject(Object **ppObj)" ist häßlich.

    Stefan.



  • @<StefanD>
    Schweig, Ungläubiger !!! 😉

    Nein jetzt mal im Ernst, ich habe mir nun extra eine viertelstunde Zeit genommen um 2 Codebeispiele zu schreiben (das erste auf die

    konventionelle Art

    , das zweite auf die

    perverse

    Art, welche den Sinn absolut erklären sollten:

    // *********************************** (Schöne Methode ???) ***********************************
    // ----------------------------- declaration of the needed classes ----------------------------
    class Object;
    class Engine;
    // --------------------------------------------------------------------------------------------
    
    // --------------------- definition and implementation of the class Object --------------------
    class Object{
    private:
        int a,b,c,d,e,f,g,h;
    public:
        Object(int a,int b,int c,int d,int e,int f,int g,int h);
        ~Object(void){}
    };
    
    Object::Object(int a,int b,int c,int d,int e,int f,int g,int h){
        this->a = a;
        this->b = b;
        this->c = c;
        this->d = d;
        this->e = e;
        this->f = f;
        this->g = g;
        this->h = h;
    }
    // --------------------------------------------------------------------------------------------
    
    // --------------------- definition and implementation of the class Engine --------------------
    class Engine{
    private:
        int a,b,c,d,e,f,g,h;
    public:
        Engine(void);
        ~Engine(void){}
        int getA(void);
        int getB(void);
        int getC(void);
        int getD(void);
        int getE(void);
        int getF(void);
        int getG(void);
        int getH(void);
    };
    
    Engine::Engine(void){
        this->a = 0x34;
        this->b = 0xf1;
        this->c = 0x89;
        this->d = 0xb3;
        this->e = 0x1c;
        this->f = 0xe3;
        this->g = 0x5d;
        this->f = 0x28;
    }
    
    int Engine::getA(void){
        return this->a;
    }
    
    int Engine::getB(void){
        return this->b;
    }
    
    int Engine::getC(void){
        return this->c;
    }
    
    int Engine::getD(void){
        return this->d;
    }
    
    int Engine::getE(void){
        return this->e;
    }
    
    int Engine::getF(void){
        return this->f;
    }
    
    int Engine::getG(void){
        return this->g;
    }
    
    int Engine::getH(void){
        return this->h;
    }
    // --------------------------------------------------------------------------------------------
    
    // ----------------------------- entry - point to the application -----------------------------
    int main(){
        Engine engine;
    
        // it is absolutly important, not to manipulate these critical values, otherwise the application will crash and possibly permanently damage the system
        Object obj(engine.getA(),engine.getB(),engine.getC(),engine.getD(),engine.getE(),engine.getF(),engine.getG(),engine.getH());
        return 0;
    }
    // --------------------------------------------------------------------------------------------
    // ********************************************************************************************
    
    // ********************************** (Hässliche Methode ???) *********************************
    // ----------------------------- declaration of the needed classes ----------------------------
    class Object;
    class Engine;
    // --------------------------------------------------------------------------------------------
    
    // --------------------- definition and implementation of the class Object --------------------
    class Object{
        friend Engine;
    private:
        int a,b,c,d,e,f,g,h;
        Object(){}
        ~Object(void){}
    public:
        void release(void);
    };
    
    void Object::release(void){
        delete this;
    }
    // --------------------------------------------------------------------------------------------
    
    // --------------------- definition and implementation of the class Engine --------------------
    class Engine{
    private:
        int a,b,c,d,e,f,g,h;
    public:
        Engine(void);
        ~Engine(void){}
        Object *createObject(void);
    };
    
    Engine::Engine(void){
        this->a = 0x34;
        this->b = 0xf1;
        this->c = 0x89;
        this->d = 0xb3;
        this->e = 0x1c;
        this->f = 0xe3;
        this->g = 0x5d;
        this->f = 0x28;
    }
    
    Object *Engine::createObject(void){
        Object *pObj = new Object;
        pObj->a = this->a;
        pObj->b = this->b;
        pObj->c = this->c;
        pObj->d = this->d;
        pObj->e = this->e;
        pObj->f = this->f;
        pObj->g = this->g;
        pObj->h = this->h;
        return pObj;
    }
    // --------------------------------------------------------------------------------------------
    
    // ----------------------------- entry - point to the application -----------------------------
    int main(){
        Engine engine;
    
        // now it is impossible for the user to manipulate critical values
        Object *pObj = engine.createObject();
        pObj->release();
        return 0;
    }
    // --------------------------------------------------------------------------------------------
    // ********************************************************************************************
    

    [ Dieser Beitrag wurde am 05.06.2003 um 15:04 Uhr von Ishildur editiert. ]



  • Jaja, ungläubig........ *g*

    Vielleicht stehe ich ja auf dem Schlauch, aber ich versteh den Sinn trotzdem nicht:
    - Warum kan ein Objekt nicht einfach mit einer const Engine& erzeugt werden und sich die Werte selbst von dort besorgen? Das geht in beiden Beispielen.
    - Warum baruch ein Object überhaupt ein Engine? Ich meine, Engine initialisiert sich seine Instanzvariablen, die Object dann einfach kopiert. Warum initialisiert sich Object seine Variablen nicht einfach genau so, wie es Engine tut. Auch dann wären die Werte ja gekapselt.
    Oder liegen diese Einwände jetzt bloß am Beispiel?

    BTW: void-Parameterlisten sind in C++ mega out.

    Stefan.



  • this->membervariable ist mega out.



  • class factory;
    
    class baseclass {
       friend class factory;
       void initialize () { final_class = is_final(); }
    
    protected:
       virtual ~baseclass () { if (final_class) { fin(this); } }
       baseclass () {}
    
       virtual bool is_final () = 0;
    
       typedef void (*FINALIZER) (baseclass*);
       FINALIZER fin;
    
    public:
       bool final () const { return final_class; }
    };
    
    class NotFinal : public baseclass {
       bool is_final () { return false; }
    };
    
    void finalize (baseclass *);
    
    class Final : public baseclass {
       Final () { fin = &::finalize; }
       bool is_final () { return true; }
       void soft_finalize () { /*bla*/ }
    };
    
    void finalize (baseclass *b) {
      static_cast<Final*>(b)->soft_finalize();
    }
    
    class factory {
    public:
         baseclass *get_instance (int class_type) {
              baseclass *r;
              switch (class_type) {
              case 0: 
                   r = new NotFinal;
                   r->initialize();
                   return r;
              case 1: 
                   r = new Final;
                   r->initialize();
                   return r;
              default: return 0;
         }
         void release_instance (baseclass *b) { delete b; }
    

    ho

    was ich eigentlich sagen wollte, ist: virtueller Konstruktor



  • @<StefanD>
    Weisst du, ich lasse mich gerne belehren und bin immer dankbar für alternativ - vorschläge ! Also wie würdest du es denn machen, damit die der Benutzer der API niemals zugriff auf eine der variablen a bis h haben ?

    @<!>
    Wieso soll das aut sein ? Wie soll denn sonst zwischen den member - variablen und den lokalen unterschieden werden ??

    Gruss Ishildur

    [ Dieser Beitrag wurde am 05.06.2003 um 23:13 Uhr von Ishildur editiert. ]



  • @Ishildur
    Ich meinte das wirklich so: Vielleicht verstehe ich dein Problem gar nicht, vielleicht stehe ich auf dem Schlauch und was du machst ist völlig richtig.

    Ich habe den Eindruck, daß du dem "private" mißtraust. Du gibst dein Object in fremde Hände und möchtest nicht daß diese fremden Hände Schindluder mit kritischen Werten treiben. Deshalb genügt es dir nicht, daß die Variablen in Object private sind, da könnte man mit fiesen Tricks ja trotzdem dran.

    Falls ich richtig rate: Vergiss es! Es ist nicht dein Job, Sicherheit zu schaffen, die über private hinausgeht. Wenn tatsächlich jemand so bescheuert ist, diese Hürde durch unlautere Manipulationen zu durchbrechen, ist das seine Sache. Du solltest dein Programm so schreiben, daß ein Benutzer deines Codes nicht _versehentlich_ Fehler damit macht. Du solltest deine Absichten deutlich machen und dich darauf verlassen, daß man sich an private hält. Alles andere macht den Code nur unübersichtlich und schwer verständlich.

    Wenn du es sinnvoll findest, die "zufällige" Instanziierung von Object zu verhindern, dann baue eine Factory, so wie virtual sie vorschlägt. Oder, falls die Factory nicht abstrakt sein (also alle möglichen Unterklassen von Object liefern können) soll, gib Engine eine static Methode, die Objects produziert. Das geht schnell, ist übersichtlich und es genügt. Oder gib Object tatsächlich einen public Konstruktor, wenn die Objekte sich die benötigten Daten selbst (z.B. von Engine) besorgen können. IMHO sollten Factories übrigens ihre Objekte per std::auto_ptr zurückliefern. Das sagt (ohne Worte *g*), daß der Aufrufer für die Entsorgung der Objekte zuständig ist.

    Grundsätzlich würde ich immer die einfachste Lösung bevorzugen, die funktioniert. Und die sauber programmiert ist.

    Zu deiner Frage bezüglich der Verwendung von this ("this->a = a;"): Du brauchst es, um auf Instanzvariablen zuzugreifen, die genauso wie Parameter heißen. Das ist wirklich keine gute Idee! Es lockt Fehler an, wie Scheiße die Fliegen. Es stimmt, Parameter sollen Namen haben, die auf die Attribute, für die sie stehen hinweisen. Schon wieder: Übersichtlichkeit. Aber wenn du sie genauso wie die Instanzvariablen benennst, kommt früher oder später der Zeitpunkt, an dem Du irgendwo das "this->" vergißt und tagelang brauchst, um den Fehler zu finden.

    Viele kennzeichnen deshalb die Instanzvariable mit einem Präfix. "m_" sieht man recht häufig, dann würde man in deinem Beispiel also "m_a = a;" schreiben. Ich selbst verwende einfach "_" (also "_a = a"). Genauso kann man auch die Parameter kennzeichnen: "a = aParm" oder sowas. Bloß _irgenwie_ sollten Parameternamen und die Namen von Instanzvariablen verschieden sein.

    Oh, und noch etwas: Verwende Initialisiererlisten:

    Object::Object(int a, int b)
        : _a(a)
        , _b(b)
    {
        // ....
    }
    

    Das ist Konvention und unter manchen Umständen ebenfall sicherer.

    Stefan.



  • @StefanD
    Hi Stefan, ich glaube du hast mein Problem wirklich nicht verstanden (nicht bös gemeint !!) Weisst du die Variablen a - h sind in der Engine gespeichert und müssen an jedes Objekt weitergegeben werden. Nur sind dies eben kritische Adressen, welche, wenn man sie verändert, wirklich das System permanent beschädigen können, da DirectX eine LowLevel API ist und teilweise den Speicherschutz von Windows umgeht. Es ebenfalls notwendig, dass der Benutzer (derjenige, der mit der Engine ein Spiel schreiben soll) diese Variablen niemals zu sehen bekommt zwecks vollständiger Kapselung der DirectX - API. Die hat den Zweck, dass Spiele die mit dieser Engie geschrieben wurde, immer noch tadelfrei laufen, wenn die Engine z.B. für OpenGl reprogrammiert wurde.

    Wegen dem this operator
    Wenn ich einen Quellcode liese, sind die Schlüsselwörter eine extreme Hilfe, mich innerhalb von Codes zu orientieren. Ich habs mal eine Zeitlang so gemacht, wie du beschriben hast, doch ich merkte schnell, dass ich auf diese Weise sehr viel mehr Mühe hatte, den Code zu lesen. Ich könnte z.B. nicht ohne Synthax - hervorhebung programmieren, da wäre ich verloren !! 😉

    Gruss Ishildur

    P.S.
    Hier noch mal ein Fallbeispiel wie der Benutzer die kritischen Werte manipulieren könnte

    // ----------------------------- entry - point to the application -----------------------------
    int main(){
     Engine engine;
    
     // it is absolutly important, not to manipulate these critical values, otherwise the application will crash and possibly permanently damage the system
     Object obj(0x35 /* and out !!! */,engine.getB(),engine.getC(),engine.getD(),engine.getE(),engine.getF(),engine.getG(),engine.getH());
     return 0;
       // have a nice day by beeing forced to reinstall your windows
    }// --------------------------------------------------------------------------------------------
    

    [ Dieser Beitrag wurde am 07.06.2003 um 17:29 Uhr von Ishildur editiert. ]


Anmelden zum Antworten