Value type



  • @Shade of Mine: gut zusammengefasst. Full ACK.



  • Ja, klingt vernünftig.
    Ich meinte auch die "keine Invarianten, alles public" Teile.



  • ich stimme dem auch zu, würde sogar noch weiter gehen: soche "bundle-of-data" structs sollten bis auf ganz wenige Ausnahmen keinerlei Methoden haben. (und was sie an Methoden haben sollte auch durchgehend public sein)

    Der Grund: Der Sinn von Methoden ist meist, Funktionalität zu liefern und gleichzeitig bei Zugriffen auf die Daten einer Klasse Invarianten zu gewährleisten. bei public-structs gibts aber keine Invarianten und die Funktionalität besteht daraus, Werte zu halten.

    Die einzigen Methoden die man sich vorstellen kann sind also:
    - Konstruktoren, um konstante Member und Member ohne Default-Ctor angemessen zu initialisieren
    - Methoden, die häufig benutzte "Massenzugriffe" bündeln und keine zusätzliche Funktionalität liefern. Solche Methoden verstoßen allerdings gegen das Prinzip der minimalen Schnittstelle (die public member SIND die Schnittstelle) und sind nicht mehr als ein Kompromiss an die Faulheit des Entwicklers 😉



  • Wie sieht es eigentlich mit solchen POD-Structs aus, wenn man da nur Konstruktoren und Destruktor reinschreibt, hat das irgendwelche Auswirkungen auf das Speichermanagement bzw. memset, memcpy etc.?
    Eigentlich dürfte das doch nicht stören, oder?



  • POD structs sind nicht das Selbe wie structs die nur zur Datenbündelung dienen.
    PODs haben afaik keine eigenen Konstruktoren außer den standardmäßig gelieferten, und sie bestehen nur aus nativen datentypen.
    public Datenbündel dagegen können sehr wohl auch andere Typen beinhalten, Bsp:

    struct FunnyBundle
    {
      const std::string name;
      int someInt;
      Foo someFoo;
    
      FunnyBundle(std::string s) : name(s) {}
    };
    

    Das FunnyBundle hat keinen Default-Ctor, ist nicht mit C-memXXX-Funktione zu behandeln usw.
    Destruktoren sollten Bündel-structs übriges auch nicht haben, da müssen die member defualt-zerstörbar sein. Alles was ein Destruktor kann (z.B. irgendwelche Ressourcen freigeben), kann auch vor dem Dtor-Aufruf schon passiert sein. Wenn man sich andererseits darauf verlassen muss, dass ein Dtor irgendeine Arbeit macht, ist das eine Invariante die zur Folge hat dass man alles in eine private klasse kapseln sollte. Die einzige Invariante die man tolerieren kann ist constness von einzelnen membern.

    auch bei POD-structs würde ich keine memXXX-Funktionen verwenden sondern die normalen Zugriffe bzw. compilergenerierten Copy-Ctoren und andere C++-Features nutzen. Wenn ich z.B. bei ner memXXX-Funtkion irgendwo mitten im POD (z.b. nach dem 7ten byte eines double) schluss mache hab ich zwar keine Specherlecks, Objektleichen etc. (die nativen Datentypen haben keine ungültigen zustände und PODs keine invarianten), dafür aber ne Menge Datenmüll, z.B. sinnfreie Zahlenwerte und Pointer ins Nirvana. Man darf ein POD zwar mutwillig verhunzen (dazu sind die Member ja public), aber wenn ich das wirklich will mache ich das deutlich und nicht hintenrum mit irgendwelchen C-Funktionen 😉



  • Hab bisher auch noch keine De- oder Konstruktoren dort eingesetzt. Nur kämpfe ich mich meist durch Structs die nur andere Structs und primitive Datentypen beinhalten usw. Dies bis zu einer Tiefe von ca. 20 Schritten oder mehr inklusive Vererbung und dort geht es dann ebenso weiter. Natürlich sind auch Pointer dabei auf eben solche weiteren Strukturen.

    Da dann ohne Destruktor zu arbeiten ist ziemlich deprimierend, da an denen Stellen wo gelöscht wird, auch immer die ganzen Member manuell durchgegangen werden müssen. Die Verwendung eines Destruktors (sei es auch nur eine stumpfe Methode die das übernimmt) liegt da natürlich nahe.



  • Fellhuhn schrieb:

    mehr inklusive Vererbung und dort geht es dann ebenso weiter.

    Structs mit vererbung sind keine PODs mehr.
    Wenn du Member explizit zerstören musst mittels Destruktor dann sind diese Member keine PODs. structs sind keine PODs mehr, wenn sie Pointer beinhalten die Besitzrechte an den referenzierten Objeten haben (sprich wenn das struct für die Zerstörung der referenzierten Objkete zuständig ist).

    Wenn ein Objekt nicht ohne weiteres leise wegsterben kann ist das ein Hinweis darauf dass die beinhalteten Daten gekapselt werden müssen. Den Pointer-Fall kann man noch flicken indem man ein Datenbündel mit Smart-poitnern draus macht.



  • Pumuckl: „Keine Methoden“ halte ich grundsätzlich für eine schlechte Idee. Zu einer Struktur gehörige Methoden gehören nunmal logisch zusammengruppiert. In C++ würde man das anders machen, aber in C# gehören diese Methoden dann eigentlich *in* die Struktur.

    Die Invarianten lassen sich leicht gewährleisten, wenn man die Strukturen immutable macht. Das ist sowieso meist eine gute Idee.



  • Konrad Rudolph schrieb:

    Pumuckl: „Keine Methoden“ halte ich grundsätzlich für eine schlechte Idee. Zu einer Struktur gehörige Methoden gehören nunmal logisch zusammengruppiert. In C++ würde man das anders machen, aber in C# gehören diese Methoden dann eigentlich *in* die Struktur.

    Die Invarianten lassen sich leicht gewährleisten, wenn man die Strukturen immutable macht. Das ist sowieso meist eine gute Idee.

    Mit Methoden meinte ich Methoden im erweiterten Sinn, also grundsätzlich jede Funktion, die logisch zu einer Klasse gehört (z.B. op<< und andere Knadidaten, die standardmäßig außerhalb der Klasse deklariert, aber mit ihr ausgeliefert werden).
    Methoden bearbeiten die Member des Structs. Da aber eh alle Member öffentlich zugänglich sind, kann diese Bearbeitung auch ohne die Methoden geschehen, somit sid die Methoden wie oben gesagt ein Erweiterung der Schnittstelle.



  • Shade Of Mine schrieb:

    Entweder sind alle Daten-Member public oder private. Egal was man macht, alle Daten-Member müssen die selben Access Rechte haben.

    Wie siehts mit statischen Membern aus? Vor allem statische Konstanten und Enums sollten doch sicher public sein. Bei statischen, nicht-konstanten Klassenvariablen kann man sich drüber streiten. Allerdings würde ich das so oder so unabhängig von den Instanzvariablen sehen.



  • Nexus schrieb:

    Wie siehts mit statischen Membern aus? Vor allem statische Konstanten und Enums sollten doch sicher public sein. Bei statischen, nicht-konstanten Klassenvariablen kann man sich drüber streiten. Allerdings würde ich das so oder so unabhängig von den Instanzvariablen sehen.

    konstanten sind keine variablen. Alles was konstant ist, kann ruhig public sein. sofern sich der wert nie ändert (zB wird erst später initialisiert oder so).

    statische variablen sollten aber wie gehabt zu den anderen access rechten passen.

    wobei man hier uU einen schritt weiter gehen will und alle statischen variablen private deklarieren will. wenn man genauer bedenkt, dann kenne ich aber keinen fall indem eine struct mit lauter public variablen eine statische member variable hatte... insofern ist statische variablen immer private machen sicher nicht verkehrt.



  • Shade Of Mine schrieb:

    insofern ist statische variablen immer private machen sicher nicht verkehrt.

    Ich hatte da einen Fall, wo ich kein Singleton verwenden wollte, und deshalb bei einer Klasse alles statisch gemacht habe - nicht gerade das ideale Design, ich weiss (aber mir gefällt der Operator :: eben so, und vereinheitlicht auch alles, wenn ich noch Enums habe). 😉

    Nein, das mit den einheitlichen Zugriffsrechten klingt für mich eigentlich logisch (ich wüsste auch keinen Fall, wo man statische Variablen öffentlich bräuchte).

    Noch was zu Strukturen: Das ist vielleicht Ansichtssache, aber für mich ist eine struct , die mehr als nicht-statische Membervariablen und einen Konstruktor anbietet, schon eher eine Klasse ( class ).

    Shade Of Mine schrieb:

    Alles was konstant ist, kann ruhig public sein. sofern sich der wert nie ändert (zB wird erst später initialisiert oder so).

    Sorry, ich kann mir jetzt nicht gerade vorstellen, wie man auf normalem Wege eine konstante, nicht-statische Variable nach dem Konstruktor initialisieren kann.

    Btw:

    Shade Of Mine schrieb:

    konstanten sind keine variablen.

    Begrifflich gesehen stimmt das zwar, aber ich bezeichne auch Konstanten per const als Variable, weil sie Werte aufnehmen können (das ist zwar eine ungenaue Definition, aber ihr wisst, was ich meine). Aber das ist meine Sichtweise, und in C++ sind sowieso einige Begriffe nicht ganz eindeutig...


Anmelden zum Antworten