Klasse: Überall "this->" vorkleben?



  • Shade Of Mine schrieb:

    Ich rede von jeder modernen Sprache. Egal welche. Ruby? PHP? Python? C#? Go? ... Die Liste ist unendlich lang. [...] Denk einfach darüber nach warum keine einzige moderne Sprache memberqualifzierungen verwendet. Keine einzige.

    Das stimmt so nicht ganz. In Ruby werden Membervariablen mit einem "@" gekennzeichnet. Da ist die Lage aber wiederum anders als in C++ und ich denke auch nicht vergleichbar. Denn in Ruby kann ich Variablen benutzen, ohne sie vorher zu definieren. Daher brauche ich auch ein Mittel, um bei einem vorher unbekannten Namen signalisieren zu können, welchen Scope die Variable hat.

    Nexus schrieb:

    wie weit man ohne Setter kommt, haben wir auch erst kürzlich besprochen.

    Ich habe die Diskussion nur am Rande verfolgt, aber das Fazit war glaube ich, dass reine dumme Setter im engeren Sinne kaum einer benutzt.

    Im Grossen und Ganzen klingt die Argumentation wie "ich brauche kein const, da ich im Blick habe, welche Variablen ich verändern darf".

    Vorsicht, ganz andere Baustelle. Man kann Klassen und dazugehörige Memberfunktionen so schreiben, dass man die Übersicht über die definierten Variablen behält. Das ist eine lokale Sache und wie ich private Member benenne, geht niemanden außerhalb der Klasse etwas an. Wenn ich aber auf const verzichte, hat das globale Auswirkungen. Objekte werden von einem Ende der Applikation zum anderen Ende übergeben. Hier versuche ich, eine möglichst aussagekräftige Schnittstelle der Klasse zur Außenwelt herzustellen, die mitteilt, ob und wie an die Klasse übergebene Parameter geändert werden können. Die Benennung von Membervariablen ist also eine lokale Angelegenheit innerhalb der Klasse, während die Benutzung von const gerade für die Schnittstelle nach außen in die große weite Welt gebraucht wird.

    Aber statt das Offensichtliche zu sagen, nämlich dass es auch ohne m-Präfixe geht, wären Gegenargumente hilfreich.

    Präfixe sind potthässlich und Intellisense liefert mir schneller den Namen, den ich suche. Das reicht mir Begründung :p



  • Shade Of Mine schrieb:

    Es funktioniert nämlich nicht. Du hast soviele Scopes und du suchst dir jetzt einen dieser Scopes (Klasse) aus um die Variablen diesen Scopes zu Kennzeichnen.

    Der Member-Scope ist neben lokalen Variablen aber der am häufigsten benutzte, und nicht willkürlich einer gewählt. Daher ist mit m-Präfixen schon ein grosser Schritt getan. Allerdings hast du recht, konsequenterweise müsste man auch globale Variablen mit g und statische mit s kennzeichnen, allerdings kommen diese ohnehin recht selten vor.

    Shade Of Mine schrieb:

    und N Stufen Funktionslokale Variablen (denke an Closures).

    Meinst du Lambda-Ausdrücke? Wo hast du da eine neue Scopekategorie? Sowohl inner- als auch ausserhalb sind für mich lokale Variablen. Nur werden die Funktionen halt zu unterschiedlichen Zeitpunkten aufgerufen. Aber wahrscheinlich habe ich dich hier falsch verstanden...

    Shade Of Mine schrieb:

    Aber du hast auch ohne dem allen eine inkonsistenz: Template Parameter. Kennzeichnest du diese auch? Sie sind ja Member der Klasse.

    Nein, sie sind "Member" des Templates. Sobald du auf der Ebene der Klasse bist, hast du eine Instanziierung, damit gibt es keine Templateparameter mehr. Da dies meist Typen sind, schreibe ich sie in PascalCase (sofern sie überhaupt länger als ein Buchstabe sind).

    Michael E. schrieb:

    Ich habe die Diskussion nur am Rande verfolgt, aber das Fazit war glaube ich, dass reine dumme Setter im engeren Sinne kaum einer benutzt.

    Ja, meist gilt das. Komplett ohne kommt man aber nicht aus. Und teilweise sind auch "dumme" Getter sinnvoll, siehe z.B. sf::Sprite.

    Michael E. schrieb:

    Die Benennung von Membervariablen ist also eine lokale Angelegenheit innerhalb der Klasse, während die Benutzung von const gerade für die Schnittstelle nach außen in die große weite Welt gebraucht wird.

    Das stimmt, aber du kannst das gleiche Argument auf const-qualifizierte lokale Variablen anwenden. Diese sind ebenfalls Implementierungsdetails, trotzdem können sie Fehler innerhalb der Funktion vermeiden -- auch wenn man Funktionen übersichtlich schreibt.


  • Mod

    Nexus schrieb:

    Das stimmt, aber du kannst das gleiche Argument auf const-qualifizierte lokale Variablen anwenden. Diese sind ebenfalls Implementierungsdetails, trotzdem können sie Fehler innerhalb der Funktion vermeiden -- auch wenn man Funktionen übersichtlich schreibt.

    Benutzt du const-qualifizierte lokale Variablen?



  • Nexus schrieb:

    Michael E. schrieb:

    Ich habe die Diskussion nur am Rande verfolgt, aber das Fazit war glaube ich, dass reine dumme Setter im engeren Sinne kaum einer benutzt.

    Ja, meist gilt das. Komplett ohne kommt man aber nicht aus. Und teilweise sind auch "dumme" Getter sinnvoll, siehe z.B. sf::Sprite.

    Für die berühmten Ausnahmen dieser Regel habe ich das Wort "eigentlich" bemüht. Aber das ist eine ganz andere Baustelle und unabhängig von diesem Thema, da ich auch bei Setterparametern, wenn sie denn mal vorkommen, den abschließenden Unterstrich benutzen kann, statt ihn bei allen Membervariablen zu benutzen.

    Das stimmt, aber du kannst das gleiche Argument auf const-qualifizierte lokale Variablen anwenden. Diese sind ebenfalls Implementierungsdetails, trotzdem können sie Fehler innerhalb der Funktion vermeiden -- auch wenn man Funktionen übersichtlich schreibt.

    Hier halte ich es wie SeppJ. Benutzt du const für lokale Variablen, die nicht von Anfang an Konstanten sind? Also ich tu das nicht.



  • Michael E. schrieb:

    Benutzt du const für lokale Variablen, die nicht von Anfang an Konstanten sind? Also ich tu das nicht.

    Also ich benutze manchmal const&-Variablen

    ctype<char> const& char_type = use_facet<ctype<char> >(cout.getloc());
    

    Allerdings auch nur, weil ich muss.



  • SeppJ schrieb:

    Benutzt du const-qualifizierte lokale Variablen?

    Ich wollte schon mit "ja" antworten, aber beim Anschauen einiger meiner Codes habe ich festgestellt, dass ich selten lokale Konstanten habe. Wenn, dann vor allem um Literalen einen sinnvollen Namen zu geben.

    Konsequenterweise hätte man fast überall const , auch in ungefährichen Situationen. Dadurch verliert es Aussagekraft und verkompliziert Code. Bei "m" habe ich dieses Gefühl nicht, der Lesefluss wird nicht unterbrochen und Ausdrücke bleiben klein.



  • Nexus schrieb:

    Konsequenterweise hätte man fast überall const , auch in ungefährichen Situationen. Dadurch verliert es Aussagekraft und verkompliziert Code. Bei "m" habe ich dieses Gefühl nicht, der Lesefluss wird nicht unterbrochen und Ausdrücke bleiben klein.

    Hat auch niemand behauptet. Du hast mit dem const-Vergleich angefangen :p

    Edit: Nur, um das nochmal klarzustellen: Ich zwinge niemanden dazu, meine Variante für die beste zu halten. Wer in seinem Code Prä- oder Suffixe verwenden will, soll das meinetwegen tun (vor allem wenn ich den Code nie zu Gesicht bekomme 😉 ). Ich habe lediglich geschrieben, warum ich für mich persönlich keinen Vorteil darin sehe.



  • Ich finde es toll, wie hier direkt von einer Qualifizierung für Scopes geredet wird.
    Ich bin leider gezwungen, die Membervariablen besonders zu kennzeichnen, weil folgendes nicht geht:

    class foo
    {
        size_t size;
    public:
        size_t size();
    }
    

    Deswegen bekommen die bei mir einen Unterstrich am Ende. Und zwar nur deswegen.



  • Nexus schrieb:

    Shade Of Mine schrieb:

    und N Stufen Funktionslokale Variablen (denke an Closures).

    Meinst du Lambda-Ausdrücke? Wo hast du da eine neue Scopekategorie? Sowohl inner- als auch ausserhalb sind für mich lokale Variablen. Nur werden die Funktionen halt zu unterschiedlichen Zeitpunkten aufgerufen. Aber wahrscheinlich habe ich dich hier falsch verstanden...

    Ja, meine ich. Lokale Funktionen.

    Du erzeugst damit neue Scopes. Warum kennzeichnest du diese nicht?

    denk einmal ueber die unterschiedlichen scopes nach. durch templates verdoppelt sich die scope anzahl ja auch wieder:

    template<typename s1>
    class C {
      int s2;
    public:
      template<typename s3>
      void foo(int s4) {
        int s5;
        for(int s6=0; s6<10; ++s6) {
          int s7;
          bar([](){int s8;});
        }
      }
    };
    

    jede Variable hat einen eigenen Scope. ich bin bis level 8 gekommen ohne auch nur irgendwie verschachtelten oder komischen code zu haben.

    Was macht den scope s2 so besonders im vergleich zu sagen wir mal s1?
    Oder warum ist s5 nicht anders gekennzeichnet als s7? es ist doch innerhalb der Schleife relevant ob die Variable am ende des Schleife noch existiert (s5) oder zumindest den Wert zwischen den durchlaeufen behaelt (s6) oder jedesmal neu initialisiert wird (s7).

    Ich habe noch nichtmal das captures von variablen angesprochen. dadurch kannst du ploetzlich variablen in andere scopes importieren...

    @Nathan:
    das ist ein Problem mit der namensgebung. In den meisten Sprachen verwendet man getSize() statt size() bzw. gleich echte Properties.



  • Ich denke solange man Coding Styles nicht übertreibt und diese selbsterklärend sind, sind sie fast alle in Ordnung. Nicht wie in einem Projekt das ich sah: CFestgelegteTranslationPxyzJRxbJzRz

    Ich klebe gerne this-> vor Klassen-Members dran und stelle globalen Variablen ein g voran. Natürlich kann man jedem einzelen Scope ein Präfix zuordnen. Aber das macht die Sache realtiv schnell unübersichtlich.

    In kenne ein paar C/C++ Projekte wo es sehr viele globale Variablen gibt und da ist es einfach schön anhand des Namens festzustellen ob diese global sind oder nicht. Einfach aus Gewohnheit.

    Dumme Frage in die Runde: Wer von euch stellt einer Klassenbezeichnung ein t vornean: Bsp.: tPoint?


  • Mod

    Bitte ein Bit schrieb:

    Dumme Frage in die Runde: Wer von euch stellt einer Klassenbezeichnung ein t vornean: Bsp.: tPoint?

    Ich nicht, ich halte es allgemein wie Michael E.. Jedoch ist bei mir im Code schon erkennbar, was eine Typenbezeichnung ist und was nicht, weil ich eine andere Case-Konvention (meistens PascalCase) benutze.

    Begründung: Es wäre zwar nicht wirklich nötig, da aus dem Zusammenhang klar ist, was eine Typbezeichnung ist und was nicht, aber ein bisschen Groß- und Kleinschreibung ist angenehmer zu lesen und es erlaubt auch Konstrukte wie Ding ding; , die ich öfters benutze (denn oftmals ist keine weitere Beschreibung nötig, welches Ding genau ding ist).



  • SeppJ schrieb:

    Begründung: Es wäre zwar nicht wirklich nötig, da aus dem Zusammenhang klar ist, was eine Typbezeichnung ist und was nicht, aber ein bisschen Groß- und Kleinschreibung ist angenehmer zu lesen und es erlaubt auch Konstrukte wie Ding ding; , die ich öfters benutze (denn oftmals ist keine weitere Beschreibung nötig, welches Ding genau ding ist).

    👍

    In kenne ein paar C/C++ Projekte wo es sehr viele globale Variablen gibt und da ist es einfach schön anhand des Namens festzustellen ob diese global sind oder nicht.

    Na ja, das Projekt leidet vermutlich unter anderen Problemen als Code-Konventionen, richtig?



  • Shade Of Mine schrieb:

    @Nathan:
    das ist ein Problem mit der namensgebung. In den meisten Sprachen verwendet man getSize() statt size() bzw. gleich echte Properties.

    Ja, es ist ein Problem der Namensgebung.
    Gelöst habe ich das mit einem Unterstrich am Ende.



  • SeppJ schrieb:

    Begründung: Es wäre zwar nicht wirklich nötig, da aus dem Zusammenhang klar ist, was eine Typbezeichnung ist und was nicht, aber ein bisschen Groß- und Kleinschreibung ist angenehmer zu lesen und es erlaubt auch Konstrukte wie Ding ding; , die ich öfters benutze (denn oftmals ist keine weitere Beschreibung nötig, welches Ding genau ding ist).

    Ich würde da " ding d " schreiben.
    Begründung: Code sieht einheitlich aus, weil ich viel Standardbibliothek+Boost nutze. Dass es eigene Klassen sind, stelle ich durch namespaces fest.
    d ist ausserdem ein kürzerer Name als ding (und genauso vielsagend), was den Code übersichtlicher macht. Meine Funktionen sind in der Regel weniger als 10 Zeilen lang, da kennt man meistens alle Variablen.

    Unterstriche verwende ich eigentlich wie Nathan und würde dann aber immer den Getter verwenden und nie direkt auf die Membervariable zugreifen.


  • Mod

    Dann doch lieber gleich D d . Ach Quatsch. LCD-Monitore ziehen mehr Strom, wenn sie schwarze Pixel anzeigen. Da ich Schwarz auf Weiß programmiere (ökonomisch und ökologisch!) sollten also Bezeichner eine möglichst geringe Pixelzahl haben. '_" ist gut, 'i', 'I' und '1' auch. Richtig guter Code sollte also am besten so aussehen

    _ I(_1 i)
    {
     _ II;
     for(_ __ = i.i; _ != i.I; --i)   // Operator-- überladen, so dass er um 1 erhöht
       II -= *i;                      // Ebenso -=. Ist einfach weniger Pixel als +=.
     _I II;                           // Mit #define _I return
    }
    


  • rationaler coder schrieb:

    SeppJ schrieb:

    Begründung: Es wäre zwar nicht wirklich nötig, da aus dem Zusammenhang klar ist, was eine Typbezeichnung ist und was nicht, aber ein bisschen Groß- und Kleinschreibung ist angenehmer zu lesen und es erlaubt auch Konstrukte wie Ding ding; , die ich öfters benutze (denn oftmals ist keine weitere Beschreibung nötig, welches Ding genau ding ist).

    Ich würde da " ding d " schreiben.
    Begründung: Code sieht einheitlich aus, weil ich viel Standardbibliothek+Boost nutze. Dass es eigene Klassen sind, stelle ich durch namespaces fest.
    d ist ausserdem ein kürzerer Name als ding (und genauso vielsagend), was den Code übersichtlicher macht. Meine Funktionen sind in der Regel weniger als 10 Zeilen lang, da kennt man meistens alle Variablen.

    Unterstriche verwende ich eigentlich wie Nathan und würde dann aber immer den Getter verwenden und nie direkt auf die Membervariable zugreifen.

    +1



  • SeppJ schrieb:

    <getrolle>

    Achso, du schreibst lieber

    const MeinIntTypedef anfangsWertFuerDieSchleife = ZAHL_NULL;
    const MeinIntTypedef exklusiverEndWertFuerDieSchleife = anfangsWertFuerDieSchleife + ZAHL_HUNDERT;
    for (MeinIntTypedef temporaereZaehlvariableFuerDieseSchleife = anfangsWertFuerDieSchleife;
         temporaereZaehlvariableFuerDieseSchleife < exklusiverEndWertFuerDieSchleife; 
         inkrementiereMeinIntTypedef(temporaereZaehlvariableFuerDieseSchleife)
    

    statt

    for (int i=0; i<100; ++i)
    


  • d? Ist nicht Dein Ernst. Zwei Klassen, die mit demselben Namen anfangen, führen dann ja zu zwei anderen Objekten. ding ist vielsagend, es zeigt, dass es eine Instanz der Ding-Klasse ist. d sagt nichts, da gehe ich von irgendeinem temporären double-Wert oder so aus.

    Bin aber auch nicht sicher, ob das ernst gemeint war. Ich benutze einbuchstabige Bezeichner nur für Zählvariablen, bei denen die Funktion so eindeutig ist wie bei einer mathematischen Formel, sodass es in den 3-4 Zeilen dann wirklich unübersichtlich ist. Verschachtelt es sich, ist das aber schon nicht mehr angemessen, wie ich finde.

    rationalerCoder:
    Du bist wohl farbenblind oder wieso siehst Du nur schwarz/weiß? Ich stempel Dich Mal als Troll ab.



  • Naja, was ist denn Ding?
    Ist Ding z.B. Point, dann hab ich schon 1000mal Point p; gesehen und mich nicht daran gestört.
    Oder in Java ist es auch nicht ungewöhnlich, Objekte die nur in einem kleinen Scope leben, nach den groß geschriebenen Buchstaben zu benennen
    (sowas wie FileInputStream fis)


  • Mod

    Das ist doch ganz einfach zu lösen: Anstatt einem langen, beschreibenden Namen zu benutzen könnte man dem Kurznamen einfach ein kleines Präfix verpassen, das beschreibt, von welchem Typ die Variable ist. Wenn man Pp, ip und up schreibt, dann ist klar, dass jeweils ein Point, ein int und ein unsigned gemeint sind. Das gleiche sollte man der Ordnung halber am besten auch für Typnamen einführen: C für class, S für struct, t für typedef. Dieses Schema klingt ungeheuer vielversprechend 😋 .


Anmelden zum Antworten