Auf Änderungen an Variablen reagieren



  • Hi
    Vorweg möchte ich erwähnen dass ich von VB vor kurzem erst zu C++ gewechselt habe (ok ok. das wechseln wird noch n bisschen dauern 😉 )
    Ich habe eine Klasse mit diversen Attributen. Nun möchte ich verhindern, dass der Programmierer der mit diesen Klassen arbeitet direkt die Werte manipulieren kann. Es gibt bestimmte Wertbereiche welche wiederum in Abhängigkeit von anderen Attributen stehen. Kurz gesagt möchte ich direkt nachdem eine Membervariable geändert wurde, diese auf Plausibilität prüfen und gegebenfalls korrigieren. Unter VB konnte ich dies einfach realisieren indem ich die Variablenzuweisungen auf Funktionen verküpfte. So wurde jedes mal wenn eine Variable geändert wurde meine Funktion aufgerufen die ihre Checks durchführen konnte.
    Unter C++ will mir hierfür irgendwie nichts einfallen was mir gefallen würde.

    1. Möglichkeit:
    Ich kapsle die eigentlichen Attribute privat und greife über Funktionen wie

    myClass.SetMyVar(foo);
    foo = myClass.GetMyVar();
    

    zu. Daran gefällt mir die Tatsache nicht dass ich 2 verschiedene Funktionen zum Lesen und schreiben brauche. Das ganze wirklich unnötig kompliziert.

    2. Möglichkeit
    Ich führe Public und Privat Membervariablen ein. Der Anwender schreibt seine Wunschwerte in die Publicwerte, und wenn das nächste mal eine Methode genau diesen Wert braucht, ließt er die Publicvariante aus, und wenn diese gültig ist, schreibt er es in die private rein. Was mir hierbei nicht gefällt ist die Fehleranfääligkeit, da ich die Checks völlig dezentral über den gesammten Quellcode durchführen muss. Ausserdem riskant ist, wenn andere Teile auf die Varible zugreifen bevor diese gecheckt wurde, und annehmen sie sei gültig.

    3. Möglichkeit
    Ich führe für die Variablen eigene Objekte ein. Sprich, ich erstelle aus integrierten Datentypen wie int eine Klasse, bei der ich die Zuweisungsoperatoren überlade.
    Meiner Meinung nach ein ziemlicher Overflow.

    Wie macht man es richtig? Ist eine von meinen Ideen, vielleicht die richtige Lösung?

    sweety



  • Möglichkeit 1 ist eigentlich die gewöhnliche und einfachste. Was spricht denn noch dagegen?

    mfg.



  • wie gesagt. Ich finde es ein wenig unübersichtlich und schlecht leserlich. Bin es halt von VB gewohnt im "Backend" wie ich es jetzt mal nenne, alles zu perfektionieren, damit das "Frontend" so einfach wie möglich mit der Klasse arbeiten kann.



  • mach doch

    class Foo {
     private:
      int bar;
     public:
      int& getBar() { return bar; };
    };
    


  • Hi!

    Public Methoden können eine Referenz oder einen Zeiger
    auf private Daten zurückliefern, was Lesen oder
    Schreiben dieser Daten ermöglicht. Diese
    Methoden können auch Code zum Überprüfen der Daten
    enthalten oder Aktionen mit den Daten veranlassen.

    mfg
    🙂



  • KingOfTheBlueMouse schrieb:

    mach doch

    class Foo {
     private:
      int bar;
     public:
      int& getBar() { return bar; };
    };
    

    damit kann man 'bar' aber verändern. das will er doch nicht



  • Alternativ kannst du Properties verwenden.
    Hier eine mal so hingeschriebene Variante:

    #include <iostream>
    
    template<typename T>
    struct PropertyCallbackBase
    {
        virtual ~PropertyCallbackBase(){}
        virtual T & Emit(T & var) = 0;
    };
    
    template<typename T, typename C>
    struct PropertyCallback : PropertyCallbackBase<T>
    {
        typedef T & (C::*CallbackType)(T &);
    
        PropertyCallback(C * c, CallbackType meth)
            :PropertyCallbackBase<T>()
            ,m_obj(c)
            ,m_callback(meth)
        {}
    
        PropertyCallback()
            :PropertyCallbackBase<T>()
            ,m_obj(0)
            ,m_callback(0)
        {}
    
        virtual ~PropertyCallback()
        {}
    
        virtual T & Emit(T & var)
        {
            if(m_obj && m_callback)
                return ((*m_obj).*m_callback)(var);
            return var;
        }
    private:
        C * m_obj;
        CallbackType m_callback;
    };
    
    template<typename T>
    struct Property
    {
        Property()
            : on_set_value(0)
        {}
    
        virtual ~Property()
        { 
            delete on_set_value; 
        }
    
        operator T const &(){ return m_value; }
    
        template<typename C>
        void RegisterOnSetValue(C * c, T & (C::*meth)(T &))
        {
            on_set_value = new PropertyCallback<T,C>(c,meth);
        }
    
        void operator=( T const & val)
        {
            if(on_set_value)
            {
                T tmp = val;
                m_value = on_set_value->Emit(tmp);
            }
            else 
            {
                m_value = val;        
            }
        }
    
    private:
    
        PropertyCallbackBase<T> * on_set_value;             
    
        T m_value;
    };
    
    struct Test
    {
        Test(){ value.RegisterOnSetValue(this,&Test::OnSet); }
        int & OnSet(int & val){ std::cout << "Setted " << val << std::endl; return val; }       
        Property<int> value;       
    };
    
    int main()
    {
        Test t;
        t.value = 10;
        int tmp = t.value;
    }
    

    Aber wenn du das verwendest solltest du auch wissen wie man damit umgeht und was da passiert. 😉

    BR



  • net schrieb:

    damit kann man 'bar' aber verändern. das will er doch nicht

    class Foo {
     private:
      int bar;
     public:
      const int& getBar() const { return bar; };
    };
    

    bis bald
    akari



  • akari schrieb:

    net schrieb:

    damit kann man 'bar' aber verändern. das will er doch nicht

    class Foo {
     private:
      int bar;
     public:
      const int& getBar() const { return bar; };
    };
    

    das lässt sich auch verändern...

    Foo f;
       const_cast<int&>(f.getBar()) = 123;
    

    warum nicht einfach so?

    class Foo 
    {
     private:
      int bar;
     public:
      int getBar() 
      { 
          return bar; 
      }
    };
    


  • Hallo

    klar läßt sich jeder Schutz in C/C++ irgendwie aushebeln.
    Fakt ist aber das ordentlicher Const-Correctness einem Programmierer, der sich seinen eigenen Quellcode nicht gewollt selber zerschießt, sehr hilft.

    bis bald
    akari


Anmelden zum Antworten