Design Frage -> Member- oder nicht Memberfunktion



  • Mach sie doch als statische Methoden. Wegen der Kapselung.



  • Chuck schrieb:

    Mach sie doch als statische Methoden. Wegen der Kapselung.

    nein nein nein nein nein nein
    das ist schlecht fuer die kapselung



  • Aha, okay.

    Dann klär mich auf. Wieso?



  • Coord calc_top_right(Rect const& rect);     // War früher get...
    Coord calc_bottom_right(Rect const& rect);  // War früher get...
    Coord calc_bottom_left(Rect const& rect);   // War früher get...
    

    Memberfunktionen mit get_..., wegen Einheitlichkeit und Lesbarkeit

    bool has_intersection(Rect const& first, Rect const& second);
    

    Kommt drauf an. Wenn du die has_intersection -Funktion noch für mehrere andere Formen überladen hast, würde ich sie draußen lassen. Wenn's die so erstmal nur für Rect gibt, würde ich sie mit reinnehmen.

    Relation detect_relation(Rect const& first, Rect const& second);
    

    Wie bei has_intersection .

    bool is_element_of(Coord const& coord, Rect const& rect);
    

    bool Rect::contains( Coord const& ) fände ich besser.


  • Administrator

    Shade Of Mine schrieb:

    alles non member und operator== und co auch gleich non member.

    Keine Ahnung, was der op== und != in der Klasse macht. Den habe ich normalerweise immer draussen. Ist auch bei Coord draussen. Also ka ^^

    Shade Of Mine schrieb:

    waere es uU sinnvoller in der Shape Klasse nur den Mittelpunkt zu speichern und anhand dessen die ecken bestimmen zu koennen?

    Das wäre sicher sinnvoll, wenn ich mehrere verschiedene Shapes habe. Ich habe aber nur Rechtecke, bzw. eigentlich Regionen, welche aber immer rechteckig sind, und Punkte.

    Aber mit weiteren Shapes, würde ich dir wahrscheinlich zustimmen.

    Shade Of Mine schrieb:

    non member bringen dir hier einen enormen vorteil wenn du mal mehr klassen hast: calc_top_right() kann fuer alle Shapes funktionieren die ein get_width() anbieten - ohne dass du neuen code schreiben musst - einfach nur calc_top_right als template funktion.

    Naja, wenn ... derzeit aber definitiv nicht in Sicht. Allerdings, wer weiss was die Zukunft bringt 🙂

    Ich bin gespannt auf weitere Meinungen.

    Grüssli



  • Aha. Ich raff grad nur wenig.

    Ich dachte immer, wenn eine Instanz benötigt wird, dann rein in die Klasse.
    Wenn nicht, statisch und wenns noch andere Instanzen gebraucht werden, man also
    nicht weiß, zu welcher Klasse es gehört, dann global oder besser Namespace.

    \edit: Beim Vererben bringt das doch auch mehr Übersicht.



  • Shade Of Mine schrieb:

    non member bringen dir hier einen enormen vorteil wenn du mal mehr klassen hast: calc_top_right() kann fuer alle Shapes funktionieren die ein get_width() anbieten - ohne dass du neuen code schreiben musst - einfach nur calc_top_right als template funktion.

    Für welches Shape würde calc_bottom_right noch funktionieren und wirklich den bottom_right Punkt des Shapes und nicht der Boundingbox liefern?



  • Nein, alles was geht raus aus der Klasse.
    Siehe zB monoliths unstrung von Sutter, zB online: http://www.gotw.ca/gotw/084.htm oder in Effective C++ Style.
    Oder auch Meyers: http://www.ddj.com/cpp/184401197

    Alles was ich ausserhalb der Klasse habe, kann ich wiederverwenden. Generell ist eine foo.bar() Syntax furchtbar restriktiv.

    Wenn nun calc_top_right() non member ist, dann habe ich es automatisch fuer jedes Shape dass ein get_width() anbietet. ohne aufwand, einfach durch statische polymorphie.

    Die gleiche funktionalitaet koennte man erreichen indem man alles Shapes von einer abstrakten Klasse Shape ableitet und dort calc_top_right() implementiert.

    Aber ploetzlich sind wir von Shape abhaengig. Wenn ich jetzt ein calc_middle() will um den mittelpunkt zu bestimmen und der Designer von Shape hat daran nicht gedacht, dann pech gehabt.

    Hier kommen eben non member ins Spiel. Ich kann jederzeit eine calc_middle() funktion implementieren die fuer alle Shapes funktioniert. Und Shapes sind alles was ein get_width() und get_height() anbietet. Ich brauche kleine laufzeit polymorphie.

    Wir haben damit eine Moeglichkeit bestehende Klassen ohne vererbung zu erweitern. denn vererbung nur um funktionalitaet hinzuzufuegen ist boese.

    Wenn wir das in der STL zb gemacht haetten, dann koennte ich
    sort(container);
    sagen um jeden beliebigen container zu sortieren.

    Was dazu fuehrt, dass wir ploetzlich generischen Code viel einfacher schreiben koennen, weil man viel weniger spezialisieren muss.



  • Ich verstehe langsam, was du meinst und es macht auch alles Sinn.

    Aber dieses

    ohne aufwand, einfach durch statische polymorphie

    versteh ich nicht.
    Wie soll die Funktion auf einmal andere Formen aufnehmen, statt nur dem Rechteck.
    Dafür muss man sie doch von einer Basisklasse ableiten oder?

    Die gleiche funktionalitaet koennte man erreichen indem man alles Shapes von einer abstrakten Klasse Shape ableitet und dort calc_top_right() implementiert.

    Und da dachte ich, das wäre das Standardkonzept, wie es in jedem Buch steht. So kann man sich irren. Aber man lernt ja nie aus.

    \edit: ja durch Templates. Ist mir noch vorm Einschlafen eingefallen.


  • Administrator

    Chuck schrieb:

    ohne aufwand, einfach durch statische polymorphie

    versteh ich nicht.
    Wie soll die Funktion auf einmal andere Formen aufnehmen, statt nur dem Rechteck.
    Dafür muss man sie doch von einer Basisklasse ableiten oder?

    Durch Templates:

    template<typename ValueT>
    Coord calc_top_right(ValueT const& value);
    

    Grüssli



  • Shade Of Mine schrieb:

    Nein, alles was geht raus aus der Klasse.
    Siehe zB monoliths unstrung von Sutter, zB online: http://www.gotw.ca/gotw/084.htm oder in Effective C++ Style.
    Oder auch Meyers: http://www.ddj.com/cpp/184401197

    Grundsätzlich ist es mit klar, wieso man freie Funktionen Memberfunktionen vorziehen sollte.
    Nun habe ich mir mal Deinen ersten Link angesehen, und dabei sind mir dann aber Zweifel gekommen.
    Herb Sutter zieht da ja jede Menge Funktionen aus basic_string heraus und spricht an, dass die meisten der Funktionen sich auch auf andere STL-Container anwenden lassen würden.
    Wenn ich mir aber jetzt sowas wie seine empty()-Templatefunktion ansehe:

    template<class charT, class traits, class Allocator>
    bool empty( const basic_string<charT, traits, Allocator>& s )
    {
      return s.size() == 0;
    }
    

    dann ist dieses in der Form doch wieder nur für basic_string zu benutzen, oder?
    Für die verschiedenen anderen Container müsste ich dann wieder andere Versionen anbieten. Wo ist also der Vorteil?

    Ich würde das (vermutlich naiv) eher so machen:

    template<typename T>
    bool empty(const T& c)
    {
        return c.size() == 0;
    }
    


  • Grundsätzlich ist es mit klar, wieso man freie Funktionen Memberfunktionen vorziehen sollte.

    Oftmals macht auch beides sinn ...
    eine freie <template> Version die mit aehnlichen klassen auch funktioniert, und eine gebundene interne, die auf grund internas effizienter implementiert werden kann.

    Ansonsten wuerd ich mich Shade Of Mine anschliessen. Vor allen den "interfaces" nen einheitliches Gesicht verpassen. Das dankt dir spaeter der, der den code warten muss. und schoen in mindestens nen namespace kapseln ...

    Wenn man erst mal ne generische funktion hat, kann man spaeter bei der optimierung immer noch gebundene interne versionen bauen und auf die umsteigen, wenn das moeglich/ notwendig ist.

    Ciao ...



  • poasting in a design discussion thread.

    lohnt es sich noch bier kalt zustellen und knabberkram zu kaufen? 😃


  • Administrator

    sothis_ schrieb:

    lohnt es sich noch bier kalt zustellen und knabberkram zu kaufen? 😃

    1. Das ist grundsätzlich der erste Trollpost.
    2. Nein, gibt keinen Grund, kannst also ruhig wieder gehen.

    @Rest,
    Bis jetzt eigentlich keine Antworten gegen dieses Vorgehen. Meine Zweifel haben sich verflüchtigt 🙂
    Um die Klasse Rect gibt es übrigens einen namespace und somit auch um die Funktionen. Ich sollte vielleicht mal die Dokumentation umbennen von Global zu Free functions oder sowas ähnliches.

    Jetzt habe ich womöglich noch ein anderes Designproblem. Aber dazu mache ich einen neuen Thread auf, sobald ich mich nochmals selber damit gründlich beschäftigt habe.

    Danke!

    Grüssli



  • Trotzdem würde ich gerne meine Frage beantwortet haben. Auch wenn sie sich eher direkt auf die Ausführungen von Herb Sutter aus Shades Link bezieht.

    Mir ist irgendwie nicht klar, wo der Vorteil ist, wenn die Funktionen frei sind, wenn sich eh nur basic_string mit ihnen benutzen lässt. Sind die Beispiele evtl. nur schlecht gewählt?

    Wenn ich für alle Container die Funktionen wieder explizit überladen muss, kann ich sie doch eigentlich auch gleich wieder in die Klasse packen, oder?
    Mir würde sich die Frage nicht stellen, wenn er (Sutter) generellere Funktionen aufgeführt hätte, wie ich sie oben im Beispiel genannte habe.



  • Tachyon schrieb:

    Trotzdem würde ich gerne meine Frage beantwortet haben. Auch wenn sie sich eher direkt auf die Ausführungen von Herb Sutter aus Shades Link bezieht.

    Sutter geht in dem Link eben auf string ein.
    Wenn man das ganze natuerlich fuer alle Container will, dann ist ein

    template<typename ContainerT>
    bool empty(ContainerT const& cont) {
      return cont.size()==0;
    }
    

    notwendig.

    PS:
    @sothis_:
    Du koenntest hier ruhig mal etwas lernen und weniger trollposts machen. Wegen Leuten wie dir frage ich mich manchmal, warum ich das hier ueberhaupt mache. Kotzt mich an, echt.



  • Shade Of Mine schrieb:

    Sutter geht in dem Link eben auf string ein.
    Wenn man das ganze natuerlich fuer alle Container will, dann ist ein...

    Wo ist denn der Sinn, freie Funktionen für etwas zu definieren, was man ohnehin nur für einen Spezialfall benutzen kann? Ist das Beispiel nur schlecht gewählt? Mir ist das irgendwie nicht so recht klar.



  • SO weit ich das verstanden habe, geht es darum, die Kapselung soweit wie
    möglich zu erhalten.

    Und wenn ich mit 3 Methoden alles realisieren könnte, was mit dieser
    Klasse machbar sein muss, dann können alle anderen freie Funktion werden, da
    diese nur die 3 Methoden benötigen.

    Laut Sutter gibts keine Performance-Einschränkungen und wenn mal was erweitert
    werden müsste ist die ein leichteres, da man keine großartige
    Klassenhierarchie verwenden muss.



  • Shade Of Mine schrieb:

    @sothis_:
    Du koenntest hier ruhig mal etwas lernen und weniger trollposts machen. Wegen Leuten wie dir frage ich mich manchmal, warum ich das hier ueberhaupt mache. Kotzt mich an, echt.

    jetzt überreagiere doch nicht so. ist ja eine sehr angespannte stimmmung hier, dann geh ich halt wieder 🙂



  • Chuck schrieb:

    SO weit ich das verstanden habe, geht es darum, die Kapselung soweit wie
    möglich zu erhalten.

    Und wenn ich mit 3 Methoden alles realisieren könnte, was mit dieser
    Klasse machbar sein muss, dann können alle anderen freie Funktion werden, da[...]

    Ja, das ist soweit schon klar, ich hänge mich allerdings (vielleicht zu sehr) an seinem Beispiel auf.
    Das Interface der freien Funktionen ist ja nun direkt auf basic_string zugeschnitten. Da lässt sich (zumindest aus meiner sicht) nicht mehr allzu viel erweitern. Aber vielleicht verstehe ich auch nur was nicht richtig.


Anmelden zum Antworten