Zu faul für get & set Methoden?



  • Mit einer Member-Funktion

    TYP& xvar() { return _xvar; }
    

    habe ich gleichzeitig get und set Funktionalität.
    Wenn Typ aber etwas simples ist, wie z.B. ein int, dann wäre es umständlich zuerst nur die Adresse abzufragen, und dann erst den Value dort abzuholen.

    Falls es nun aber wirklich auf Geschwindigkeit ankommt, zumindest beim "get", kann ich dann annehmen, dass der Compiler alles so organisiert, dass gleich der Value zurückliefert wird, oder ist es besser 2 Funktionen zu schreiben, z.B.

    TYP   xvar()      { return _xvar; }    // für get
    TYP&  setXVar()   { return _xvar; }    // für set
    

    hgf



  • Ist da "inline" nicht zu empfehlen?



  • hgf schrieb:

    Mit einer Member-Funktion

    TYP& xvar() { return _xvar; }
    

    habe ich gleichzeitig get und set Funktionalität.

    Klar, wieso machst du sie dann nicht gleich public? 🙄

    hgf schrieb:

    Wenn Typ aber etwas simples ist, wie z.B. ein int, dann wäre es umständlich zuerst nur die Adresse abzufragen, und dann erst den Value dort abzuholen.

    Falls es nun aber wirklich auf Geschwindigkeit ankommt, zumindest beim "get", kann ich dann annehmen, dass der Compiler alles so organisiert, dass gleich der Value zurückliefert wird...

    Also hier wird jeder Compiler die Funktion wegoptimieren.

    hgf schrieb:

    oder ist es besser 2 Funktionen zu schreiben, z.B.

    TYP   xvar()      { return _xvar; }    // für get
    TYP&  setXVar()   { return _xvar; }    // für set
    

    hgf

    Es ist zumindest ein Schritt in die richtige Richtung, obwohl ich dafür bin sprechende Namen zu verwenden. Klar, wenn einem ansonsten nichts besseres einfällt muss get/set herhalten.

    Beim getter solltest du eine const-Referenz zurückliefern falls der Typ viel Speicherplatz einnimmt. Der setter ist völlig daneben. Da macht man eher so etwas...

    void setXVar(TYP xvar) { // bzw. "const TYP& xvar" bei großem Typ
      m_xvar = xvar;
    }
    

    Und Unterstriche vor eigenen Namen sind generell nicht zu empfehlen, da sie für die Compilerhersteller reserviert sind.



  • Das bricht einfach ein grundlegendes Prinzip der OOP namens Einkapselung.



  • Grundsätzlich sollte man bestrebt sein, setter zu vermeiden, insbesondere solche, die ein ganz bestimmtes Datenelement ändern. Das ist dann kaum noch besser wie public, man kann vielleicht noch den Wertebereich prüfen.
    Am besten ist es, man schreibt sich imutable classes wo immer möglich, IMO.



  • Klar, dass man setter auf ein bestimmtes Element vermeiden sollte, weil dies dem Gedanken des information hiding widerstrebt. Allerings kannst du nicht immer mit immutable classes glücklich werden, so dass setter notwendig werden. Ich plädiere allerdings dafür die Kreativität zu bemühen und sprechende Namen zu verwenden statt dem langweiligen get/set-Kram. Den kann man immer noch nehmen wenn die Kreativität einmal versagen sollte 😉 .



  • hgf schrieb:

    Mit einer Member-Funktion

    TYP& xvar() { return _xvar; }
    

    habe ich gleichzeitig get und set Funktionalität.

    Nein, nicht so richtig. Der eigentliche Vorteil von get/set-Methoden ist, dass das Objekt selber kontrollieren und beobachten kann, wann und *wie* darauf zugegriffen wird. Deine Methode kannst du fast mit einer public-Variablen ersetzen.



  • warum nicht so etwas wie properties? Mit Templates kann man das doch leicht machen

    #ifndef PROPERTIES_HH
    #define PROPERTIES_HH
    
    namespace props {
      template<typename T>
      struct nothing {
        static void set(T &) { }
      };
      template<typename T, class Policy=nothing<T> >
      class propertie {
        T obj_m;
        propertie(const propertie<T,Policy> &);
        propertie<T> operator=(const propertie<T,Policy> &);
      public:
        operator T() { return obj_m; }
        operator const T() { return obj_m; }
        const T &operator*() const { return obj_m; }
        T &operator*() { return obj_m; }
        template<typename Y, class Set_2>
        propertie<T,Policy> &
        operator=(const propertie<Y,Set_2> &obj) {
          if(&obj!=this) {
            obj_m=obj;
    	Policy::set(obj_m);
          }
          return *this;
        }
        template<typename Y>
        propertie<T,Policy> &operator=(const Y &obj) {
          Policy::set(obj_m=obj);
          return *this;
        }
        template<typename Y>
        propertie(const Y &obj) : obj_m(obj) { Policy::set(obj_m); }
        template<typename Y>
        propertie(const propertie<Y> &obj) : obj_m(obj) { Policy::set(obj_m); }
        propertie() { }
      };
    }
    
    #endif
    
    #include "properties.hh"
    #include <iostream>
    using namespace props;
    
    struct foo {
    };
    
    struct demo1 {
      propertie<int> i;
      propertie<int,demo1> j;
      propertie<foo> f;
      demo1() : i(1) { }
      template<typename Char,class Char_traits>
      void out(std::basic_ostream<Char,Char_traits> &o) const
      { o << "i:=" << *i << " j:=" << *j; }
    
      static void set(int &j) {
        j%=5;
      }
    };
    
    template<typename Char,class Traits>
    std::basic_ostream<Char,Traits> &operator<<(std::basic_ostream<Char,Traits> &o,
    					    const demo1 &obj)
    {
      obj.out(o);
      return o;
    }
    
    int main() {
      demo1 obj;
      obj.j=11;
      std::cout << obj << std::endl;
    }
    


  • Nachdem ich jetzt erstmal indent rüberlaufen lassen musste um den Code zu entschlüsseln, sieht das für mich ziemlich unbrauchbar aus. Was machst du, wenn eine Klasse mehrere Properties hat? Ausserdem fehlt dort noch die Möglichkeit auf get zu reagieren.

    Wenn dann, müsste das in der Anwendung so aussehen:

    class Foo {
      property<Foo, int> x;
    
      Foo()
        : x(this, &Foo::get_x, &Foo::set_x, 0)
      {
      }
      ...
    };
    

    Dann kommt noch das Problem, wenn 'x' hier jetzt eine Klasse wäre, deren Konstruktor mehrere Argumente nimmt.

    Aber dann doch lieber get/set-Methoden definieren und dabei gleich drüber nachdenken, ob man die Schnittstelle nicht gleich anders definiert.



  • Nachdem ich jetzt erstmal indent rüberlaufen lassen musste um den Code zu entschlüsseln, sieht das für mich ziemlich unbrauchbar aus. Was machst du, wenn eine Klasse mehrere Properties hat? Ausserdem fehlt dort noch die Möglichkeit auf get zu reagieren.

    Dann nimmst du eben nicht Kingruedis Properties, sondern meine. (Sind grad nicht online, wegen eines Umzugs).



  • Willst du mal ein Anwendungsbeispiel posten? Mein Beispiel könnte ich selber implementieren, nur was mir daran und an Kings Code nicht gefällt, ist dass man mit dem *-Operator (oder anderer Methode) darauf zugreifen muss. Hm, man könnte vielleicht einen operator T(); definieren...

    Und bei dem was ich im Kopf habe, fehlt da noch ein Weg, zur Compilezeit festlegen zu können, ob man nur get oder nur set, oder beides haben will.



  • Was machst du, wenn eine Klasse mehrere Properties hat?

    naja, man kann für jeden Typ ja eigene propertie-policies definieren. Ich hab in dem Beispiel nur einfach die eigene Klasse direkt als propertie-policie benutzt

    Ausserdem fehlt dort noch die Möglichkeit auf get zu reagieren.

    das kannst du ja ruck-zuck einbauen.

    und an Kings Code nicht gefällt, ist dass man mit dem *-Operator (oder anderer Methode) darauf zugreifen muss. Hm, man könnte vielleicht einen operator T();

    das hab ich doch implementiert



  • hab mal die Änderungen eingefügt 🙄

    #ifndef PROPERTIES_HH
    #define PROPERTIES_HH
    
    namespace props {
      template<typename T>
      struct nothing {
        static void set(T &) { }
        static T get(const T &t) { return t; }
      };
    
      template<typename T, class Set_policy=nothing<T>, class Get_policy=nothing<T> >
      class property {
        T obj_m;
        property(const property<T,Set_policy,Get_policy> &);
        property<T> operator=(const property<T,Set_policy,Get_policy> &);
      public:
        operator const T() const {
          return Get_policy::get(obj_m);
        }
    
        const T operator*() const {
          return Get_policy::get(obj_m);
        }
    
        template<typename Y, class Set, class Get>
        property<T,Set_policy,Get_policy> &
        operator=(const property<Y,Set,Get> &obj) {
          if(&obj!=this) {
            obj_m=obj;
    	Set_policy::set(obj_m);
          }
          return *this;
        }
    
        template<typename Y>
        property<T,Set_policy,Get_policy> &operator=(const Y &obj) {
          Set_policy::set(obj_m=obj);
          return *this;
        }
    
        template<typename Y>
        property(const Y &obj)
          : obj_m(obj)
        {
          Set_policy::set(obj_m);
        }
    
        template<typename Y,class Set,class Get>
        property(const property<Y,Set,Get> &obj)
          : obj_m(obj)
        { 
          Set_policy::set(obj_m);
        }
    
        property() { }
      };
    }
    
    #endif
    
    #include "properties.hh"
    #include <iostream>
    using namespace props;
    
    struct foo {
    };
    
    struct demo1 {
      struct get_policy {
        static int get(int i) { return i+1; }
      };
      static void set(int &j) {
        j%=5;
      }
    
      property<int,nothing<int>,get_policy> i;
      property<int,demo1> j;
      property<foo> f;
    
      demo1() : i(1) { }
    
      template<typename Char,class Char_traits>
      void out(std::basic_ostream<Char,Char_traits> &o) const
      { o << "i:=" << i << " j:=" << j; }
    };
    
    template<typename Char,class Traits>
    std::basic_ostream<Char,Traits> &operator<<(std::basic_ostream<Char,Traits> &o,
    					    const demo1 &obj)
    {
      obj.out(o);
      return o;
    }
    
    int main() {
      demo1 obj;
      obj.j=11;
      std::cout << obj << std::endl;
    }
    


  • kingruedi schrieb:

    naja, man kann für jeden Typ ja eigene propertie-policies definieren. Ich hab in dem Beispiel nur einfach die eigene Klasse direkt als propertie-policie benutzt

    ja schon, aber dann muss man wieder die Kommunikation zw. der Property-Besitzer-Klasse und dem get/set-Strukt schaffen.

    Hm, man könnte vielleicht einen operator T();

    das hab ich doch implementiert

    Stimmt, hab ich übersehen. Der *-Operator müsste *obj_m zurückliefern (bzw. *get(obj_m)). Oder hast du das absichtlich gemacht, damit man kontrollieren kann, ob eine Kopie oder eine Referenz zurückgegeben wird? Ist dann etwas ungewöhnlich wenn man **x schreiben muss, um zu dereferenzieren.

    // hm, in der ersten Version gibst du bei *() noch eine Referenz zurück, bei der zweiten das selbe wie bei T()...


Anmelden zum Antworten