Ist es eine gute Idee, mich zu definieren?

19

Dieses Makro kann in einem globalen Header oder besser als Compiler-Befehlszeilenparameter definiert werden:

#define me (*this)

Und ein Anwendungsbeispiel:

some_header.h:

inline void Update()
{
    /* ... */
}

main.cpp:

#include "some_header.h"

class A {
public:
    void SetX(int x)
    {
        me.x = x;   
        me.Update(); 
    }

    void SomeOtherFunction()
    {
        ::Update();
    }

    /*
        100 or more lines
        ... 
    */

    void Update()
    {
        // ... 
    }

    int x;  
};

In einer Klassenmethode verwende ich beim Zugriff auf ein Klassenmitglied immer meund beim Zugriff auf einen globalen Bezeichner immer ::. Dies gibt dem Leser, der mit dem Code nicht vertraut ist (wahrscheinlich ich selbst nach ein paar Monaten), lokalisierte Informationen darüber, worauf zugegriffen wird, ohne dass er irgendwo anders suchen muss. Ich möchte definieren, meweil ich die Verwendung this->überall zu laut und hässlich finde . Aber kann #define me (*this)eine gute C ++ - Praxis betrachtet werden? Gibt es einige praktische Probleme mit dem meMakro? Und wenn Sie als C ++ - Programmierer der Leser von Code sein werden, der das meMakro verwendet, würden Sie es mögen oder nicht?

Edit: Weil viele Leute nicht spezifisch dagegen argumentieren me, sondern generell dagegen explizit. Ich denke, es ist möglicherweise nicht klar, welche Vorteile es hat, "dies überall zu verdeutlichen".

Was sind die Vorteile von "explizit dies überall"?

  • Als Leser des Codes haben Sie die Gewissheit, auf was zugegriffen wird, und können sich auf andere Dinge konzentrieren, als zu überprüfen, ob in einem entfernten Code wirklich auf das zugegriffen wird, worauf Ihrer Meinung nach zugegriffen wird.
  • Sie können die Suchfunktion genauer verwenden. Suche " this->x" kann mehr gewünschte Ergebnisse liefern als nur Suche " x"
  • Wenn Sie ein Mitglied löschen oder umbenennen, benachrichtigt Sie der Compiler zuverlässig an den Stellen, an denen dieses Mitglied verwendet wird. (Einige globale Funktionen können denselben Namen haben und es besteht die Möglichkeit, dass Sie einen Fehler einführen, wenn Sie dies nicht explizit verwenden.)
  • Wenn Sie Code umgestalten und die Nicht-Member-Funktion von Member explizit machen (um eine bessere Kapselung zu erreichen), wird angezeigt, welche Stelle Sie bearbeiten müssen, und Sie können dies einfach durch einen Zeiger auf eine Instanz der Klasse ersetzen, die als Nicht-Member-Funktionsparameter angegeben ist
  • Im Allgemeinen gibt es beim Ändern von Code mehr Möglichkeiten für Fehler, wenn Sie dies nicht explizit verwenden, als wenn Sie dies überall explizit verwenden.
  • Explizit ist dies weniger laut als explizit „m_“, wenn Sie ein Mitglied von außerhalb ( object.membervs object.m_member) ansprechen (danke an @Kaz, um diesen Punkt zu erkennen)
  • Explizit löst dies das Problem universell für alle Mitglieder - Attribute und Methoden, wohingegen „m_“ oder ein anderes Präfix praktisch nur für Attribute verwendbar ist.

Ich würde diese Liste gerne polieren und erweitern, sagen Sie mir, ob Sie über andere Vorteile Bescheid wissen, und verwenden Sie Anwendungsfälle, um dies überall explizit zu verdeutlichen .

user3123061
quelle
3
Sie sollten vermeiden, Funktionsargumente und Member mit demselben Namen zu haben und explizites "dies" zu vermeiden.
pjc50
4
Erinnert mich an den VB-Code meines Chefs. Ich ich ich überall. Klingt egoistisch. : p Wie auch immer, die bisherigen Antworten sagen alles.
MetalMikester
19
Es ist eine wundervolle Idee! Und für diejenigen, die aus Python stammen, warum nicht #define self (*this)? Sie können sogar beide Makros mischen und einige Dateien VB und andere Python imitieren lassen. :)
logc
11
Warum nicht einfach gehen alle aus, und tun: #include "vb.h", #Include pascal.h, oder #include FOTRAN.hund haben die nächste Person , um Ihren Code zu berühren einreichen TDWTF .
Dan Neely
4
Oh bitte, nur nein. Sie könnten sich Ärger ersparen, indem Sie einfach Attribute als deklarieren me_x.
Gort the Robot

Antworten:

90

Nein ist es nicht.

Achten Sie auf den Programmierer, der Ihren Code in einigen Jahren, lange nachdem Sie sich auf grünere Weiden begeben haben, pflegt, und befolgen Sie die üblichen Konventionen der von Ihnen verwendeten Sprache. In C ++ müssen Sie fast nie schreiben this, da die Klasse in der Symbolauflösungsreihenfolge enthalten this->ist und impliziert wird, wenn ein Symbol im Klassenbereich gefunden wird. Also schreibe es einfach nicht so wie alle anderen.

Wenn Sie oft verwirrt sind, welche Symbole aus dem Klassenbereich stammen, verwenden Sie für gewöhnlich ein gemeinsames Benennungsmuster für Mitglieder (Felder und manchmal private Methoden; ich habe nicht gesehen, dass es für öffentliche Methoden verwendet wird). Häufig werden Suffixe mit _oder Präfixe mit m_oder verwendet m.

Jan Hudec
quelle
12
"In C ++ muss man das so gut wie nie schreiben" , das ist eine Konvention, die nichts mit der Frage zu tun hat. Einige Leute (wie ich) bevorzugen das Schreiben, thisum explizit zu verdeutlichen, dass die Variable für die Methode nicht lokal ist. Die Frage ist hier, ob das Makro vorhanden sein mesollte, nicht, ob thisetwas verwendet werden sollte.
Mehrdad
8
@Mehrdad: Weil das OP ausdrücklich sagt, dass er me.anstelle von verwendet this->. Da dies nicht erforderlich this->ist, ist me.das Makro nicht erforderlich und daher auch nicht erforderlich.
Martin York
11
@LokiAstari: Nichts in der Frage deutet auch nur aus der Ferne auf das OP hin und fragt sich, ob this->"notwendig" ist. Tatsächlich sagt das OP, dass er verwendet, this->obwohl es nicht notwendig ist. Die Frage ist, ob stattdessenme. verwendet werden soll this-> , was diese Antwort eigentlich nicht beantwortet.
Mehrdad
4
@Mehrdad: Aber nur Ja oder Nein zu sagen ist eine sehr langweilige Antwort. Erklärungen sind daher in der Regel sehr nützlich, um zu erklären, wie die Antwort erreicht wird (wird durch Antwortverfahren ergänzt ). Die Schlussfolgerung ist nicht zu verwenden, da das OP zu Beginn die falsche Einstellung zur Verwendung hat thisund daher eine Diskussion über diesen Punkt erforderlich ist, um eine vollständig ausgewogene Schlussfolgerung zu erzielen.
Martin York
1
+1 für die Erklärung, warum, anstatt nur zu sagen "Nein, es ist eine schlechte Praxis."
user253751
42

Sie möchten also eine neue Sprache erstellen. Dann tun Sie dies und lähmen Sie C ++ nicht.

Es gibt verschiedene Gründe, dies nicht zu tun:

  1. Jeder normale Kodierungsstandard schlägt vor, Makros zu vermeiden ( hier ist der Grund )
  2. Es ist schwieriger, Code mit solchen Makros zu verwalten. Jeder, der in C ++ programmiert, weiß, was es thisist, und durch Hinzufügen eines solchen Makros fügen Sie tatsächlich ein neues Schlüsselwort hinzu. Was ist, wenn jeder etwas vorstellt, das ihm gefällt? Wie würde Code aussehen?
  3. Sie sollten nicht (*this).oder gar nicht verwenden this->, außer in einigen besonderen Fällen (siehe diese Antwort und suche nach "this->")

Ihr Code unterscheidet sich nicht von dem #define R return, den ich im eigentlichen Code gesehen habe. Grund? Weniger tippen!


Ich gehe ein wenig vom Thema ab, aber hier werde ich auf Punkt 3 eingehen (nicht verwenden (*this).oder this->in einer Klasse).

Zuallererst (*this).oder this->werden verwendet, um auf Mitgliedsvariablen oder Funktionen des Objekts zuzugreifen. Es zu benutzen ist bedeutungslos und bedeutet mehr Tippen. Außerdem ist das Lesen eines solchen Codes schwieriger, da mehr Text vorhanden ist. Das bedeutet eine härtere Wartung.

Was sind die Fälle, in denen Sie verwenden müssen this->?

(a) Unglückliche Auswahl des Namens des Arguments.

In diesem Beispiel this->ist erforderlich, da das Argument denselben Namen wie die Mitgliedsvariable hat:

struct A {
  int v;
  void foo( int v ) {
    this->v =v;
  }
};

(b) Beim Umgang mit Vorlagen und Vererbung (siehe dies )

Dieses Beispiel kann nicht kompiliert werden, da der Compiler nicht weiß, auf welche Variable vzugegriffen werden soll.

template< typename T >
struct A {
  A(const T& vValue):v(vValue){}

  T v;
};

template< typename T >
struct B : A<T>
{
    B(const T& vValue):A<T>(vValue){}

    void foo( const T & newV ) {
      v = newV;
    }
};
BЈовић
quelle
1
"Sie möchten also eine neue Sprache erstellen. Dann tun Sie dies und lähmen Sie c ++ nicht." - Ich stimme dir nicht zu. Eine große Stärke von C und C ++ ist ihre Flexibilität. Das OP lähmt C ++ nicht, sondern nutzt seine Flexibilität nur auf eine bestimmte Weise, um ihm das Codieren zu erleichtern.
user253751
2
@immibis Richtig. Was ihm leichter fällt, fällt allen anderen schwerer. Wenn Sie in einem Team arbeiten, wird es Sie nicht sehr günstig machen, solche Unsinnigkeiten in den Code zu setzen.
BЈовић
2
Ihre Antwort unterscheidet sich nicht von "Definiert sind böse".
Mr Lister
7
@ MrLister Meine Antwort ist "dumm definiert sind böse". Das Makro in der Frage ist dumm zu benutzen und, wie ich gezeigt habe, überhaupt nicht nötig. Der Code ist gut und noch besser ohne *this.undthis->
B --овић
1
@Mehrdad Werden den Mitgliedsvariablen immer noch Präfixe und Suffixe hinzugefügt? Ich dachte, das wurde mit Büchern wie Clean Code "behoben" . this->ist so nützlich wie autoin Pre-C ++ 11. Mit anderen Worten, es erhöht nur das Code-Rauschen.
BЈовић
26

Ich schlage vor, dies nicht zu tun. Dies gibt einem Leser, der mit Ihrem Makro nicht vertraut ist, einen großen " WTF ", wenn er dies sieht. Code wird nicht besser lesbar, wenn "neue Konventionen" über die allgemein akzeptierten hinaus erfunden werden, ohne dass dies wirklich erforderlich ist.

Verwenden Sie diese -> überall ist zu laut und hässlich

Das mag Ihnen so erscheinen, vielleicht weil Sie viel in Sprachen mit dem Schlüsselwort programmiert haben me(Visual Basic, nehme ich an?). Tatsächlich ist es aber nur eine Frage der Gewöhnung - es this->ist ziemlich kurz, und ich denke, die meisten erfahrenen C ++ - Programmierer werden Ihrer Meinung nicht zustimmen. Und im obigen Fall ist weder die Verwendung von this->noch die Verwendung von meangemessen - Sie erzielen die geringste Unordnung, wenn Sie diese Schlüsselwörter weglassen, wenn Sie auf Datenelemente innerhalb von Elementfunktionen zugreifen.

Wenn Sie möchten, dass Ihre privaten Mitgliedsvariablen von den lokalen Variablen unterschieden werden, fügen Sie ihnen einen Link m_als Präfix oder einen Unterstrich als Suffix hinzu (aber wie Sie hier sehen können , ist auch diese Konvention für viele Leute "zu laut").

Doc Brown
quelle
12
_sollte angehängt werden ; als Präfix ist es für interne Symbole von Standardbibliotheken und Herstellererweiterungen reserviert.
Jan Hudec
4
@JanHudec Nur wenn es mit __(zwei Unterstrichen) oder einem Unterstrich gefolgt von einem Großbuchstaben beginnt . Ein einzelner Unterstrich, gefolgt von einem Kleinbuchstaben, ist in Ordnung, sofern er nicht im globalen Namespace enthalten ist.
Eric Finn
1
@EricFinn: Ich habe die beiden Regeln zu einer zusammengefasst. Zwei Unterstriche oder Unterstriche, gefolgt von Großbuchstaben, stehen für interne Symbole und ein einzelner Unterstrich, gefolgt von Kleinbuchstaben, für systemspezifische Erweiterungen. Anwendungen sollten auch nicht verwenden.
Jan Hudec
@JanHudec Die Regel für systemspezifische Erweiterungen war nicht bekannt. Ich werde meinen vorherigen Kommentar jedoch verlassen, um einen Kontext für Ihren Kommentar bereitzustellen.
Eric Finn
2
@JanHudec: Kleinbuchstaben sind für systemspezifische Erweiterungen? Könnten Sie einen Verweis auf den Teil des C ++ - Standards veröffentlichen, der dies anzeigt?
Mehrdad
18

Bitte tu es nicht! Ich versuche, mit einer großen Codebasis umzugehen, in der Makros überall sind, um das Schreiben zu sparen. Das Schlimme an meiner Neudefinition ist, dass der Präprozessor ihn überall dort ersetzt, wo dies nicht im Geltungsbereich liegt / nicht zutrifft, zum Beispiel als eigenständige Funktion. Möglicherweise hat Ihr Kollege eine lokale Variable, die mich irgendwo anders nennt. Er wird sich nicht über das Debuggen freuen ... Am Ende haben Sie Makros, die Sie nicht in allen Bereichen verwenden können.

Truschival
quelle
6

NEIN!

Stellen Sie sich die Verwirrung vor, die auftreten wird, wenn jemand diesen Header einbindet, Ihren Trick nicht kennt und an anderer Stelle in seiner Datei eine Variable oder Funktion namens "me" hat. Sie wären schrecklich verwirrt, wenn eine unergründliche Fehlermeldung ausgegeben würde.

Larry Gritz
quelle
In der Praxis werden sie mit ziemlicher Wahrscheinlichkeit mit der Maus über "mich" fahren und die Definition sehen.
user253751
2
@immibis: In der Praxis sagen sie wahrscheinlich auch so etwas wie "Wer hat diesen Mist geschrieben?". : P
cHao
@immibis: Die meisten der Top-Codierer, mit denen ich arbeite, benutzen nicht die Maus - sie ist zu langsam.
JBRWilkinson
In der Tat: Dies in einen Header zu schieben ist das eigentliche Problem. Wenn Sie es in Ihrer cpp-Datei verwenden, sehe ich überhaupt kein Problem. Wenn wir Pragma Push als Standard hätten, würde ich raten, wie man das in einem Header macht, aber wir tun es nicht. Sprich mir nach. #defines sind global.
Joshua