Warum Präfixe für Mitgliedsvariablen in C ++ - Klassen verwenden?

150

Viele C ++ - Codes verwenden syntaktische Konventionen zum Markieren von Mitgliedsvariablen. Häufige Beispiele sind

  • m_ memberName für öffentliche Mitglieder (wo öffentliche Mitglieder überhaupt verwendet werden)
  • _ memberName für private Mitglieder oder alle Mitglieder

Andere versuchen, die Verwendung von this-> member zu erzwingen, wenn eine Mitgliedsvariable verwendet wird.

Nach meiner Erfahrung können die meisten größeren Codebasen solche Regeln nicht konsistent anwenden.

In anderen Sprachen sind diese Konventionen weit weniger verbreitet. Ich sehe es nur gelegentlich in Java oder C # -Code. Ich glaube, ich habe es noch nie in Ruby- oder Python-Code gesehen. Daher scheint es bei moderneren Sprachen einen Trend zu geben, kein spezielles Markup für Mitgliedsvariablen zu verwenden.

Ist diese Konvention heute in C ++ noch nützlich oder ist sie nur ein Anachronismus? Zumal es in Bibliotheken so uneinheitlich verwendet wird. Haben die anderen Sprachen nicht gezeigt, dass man auf Mitgliederpräfixe verzichten kann?

VoidPointer
quelle
15
Ich bevorzuge es; In komplexen Codebasen kann es wichtig sein zu wissen, welche Vars lokal sind und welche nicht. Ich benutze im Allgemeinen das Präfix, um dies zu erzwingen-> was ich als viel zusätzliche Eingabe und optional empfinde (während die Benennung Sie dazu zwingt)
Joe
5
Sie haben es in Ruby aufgrund des Attributs @ for und der Redewendung, Accessoren zu generieren, anstatt Attribute direkt zu verwenden, noch nie gesehen.
Steve Jessop
6
Nach PEP 8 sollen nicht öffentlichen Mitgliedsvariablen in Python ein Unterstrich vorangestellt werden (Beispiel :) self._something = 1.
Nathan Osman
2
Sollte nicht die Syntaxhervorhebung des Editors verwendet werden, um diese zu identifizieren?
Auch
2
Sie haben das Äquivalent von this->memberin Python-Code gesehen. In Python ist dies normalerweise der Fall self.memberund es ist nicht nur eine Konvention, sondern wird von der Sprache benötigt.
Matec

Antworten:

48

Sie müssen vorsichtig sein, wenn Sie einen führenden Unterstrich verwenden. Ein führender Unterstrich vor einem Großbuchstaben in einem Wort ist reserviert. Beispielsweise:

_Foo

_L

sind alle reservierten Wörter während

_foo

_l

sind nicht. Es gibt andere Situationen, in denen führende Unterstriche vor Kleinbuchstaben nicht zulässig sind. In meinem speziellen Fall stellte ich fest, dass _L zufällig von Visual C ++ 2005 reserviert wurde und der Konflikt einige unerwartete Ergebnisse verursachte.

Ich bin am Zaun darüber, wie nützlich es ist, lokale Variablen zu markieren.

Hier ist ein Link darüber, welche Bezeichner reserviert sind: Welche Regeln gelten für die Verwendung eines Unterstrichs in einem C ++ - Bezeichner?

Juan
quelle
4
Tatsächlich sind sowohl _foo als auch _l im Namespace-Bereich reserviert.
13
Aber sie sind als Mitgliedsvariablennamen in Ordnung. Ich stelle keine Unterstriche voran, weil die Regeln zu verwirrend sind und ich mich in der Vergangenheit verbrannt habe.
Juan
11
Dies sind keine reservierten Wörter. Sie sind reservierte Namen. Wenn es reservierte Wörter wären, könnten Sie sie überhaupt nicht verwenden. Da es sich um reservierte Namen handelt, können Sie diese verwenden, jedoch auf eigenes Risiko.
TonyK
235

Ich bin alle für gut gemachte Präfixe .

Ich denke, dass die ungarische (System-) Notation für den größten Teil des "schlechten Rufs" verantwortlich ist, den Präfixe erhalten.

Diese Notation ist in stark typisierten Sprachen, z. B. in C ++ "lpsz", weitgehend sinnlos, um Ihnen mitzuteilen, dass Ihre Zeichenfolge ein langer Zeiger auf eine nicht terminierte Zeichenfolge ist, wenn: Segmentierte Architektur eine alte Geschichte ist, C ++ - Zeichenfolgen nach gängigen Konventionen Zeiger auf nicht terminierte Zeichenfolgen sind char-Arrays, und es ist gar nicht so schwer zu wissen, dass "customerName" eine Zeichenfolge ist!

Ich verwende jedoch Präfixe, um die Verwendung einer Variablen anzugeben (im Wesentlichen "Apps Ungarisch", obwohl ich es vorziehe, den Begriff Ungarisch zu vermeiden, da er eine schlechte und unfaire Verbindung mit System Hungarian hat), und dies ist eine sehr praktische Zeitersparnis und Fehlerreduzierender Ansatz.

Ich benutze:

  • m für Mitglieder
  • c für Konstanten / Readonlys
  • p für Zeiger (und pp für Zeiger auf Zeiger)
  • v für flüchtig
  • s für statische
  • i für Indizes und Iteratoren
  • e für Veranstaltungen

Wenn ich den Typ klarstellen möchte , verwende ich Standardsuffixe (z. B. List, ComboBox usw.).

Dies macht den Programmierer auf die Verwendung der Variablen aufmerksam, wenn er sie sieht / verwendet. Der wohl wichtigste Fall ist "p" für Zeiger (da sich die Verwendung von var. Zu var-> ändert und Sie mit Zeigern - NULL, Zeigerarithmetik usw. - viel vorsichtiger umgehen müssen), aber alle anderen sind sehr praktisch.

Beispielsweise können Sie denselben Variablennamen in einer einzigen Funktion auf verschiedene Arten verwenden: (hier ein C ++ - Beispiel, das jedoch für viele Sprachen gleichermaßen gilt)

MyClass::MyClass(int numItems)
{
    mNumItems = numItems;
    for (int iItem = 0; iItem < mNumItems; iItem++)
    {
        Item *pItem = new Item();
        itemList[iItem] = pItem;
    }
}

Sie können hier sehen:

  • Keine Verwechslung zwischen Element und Parameter
  • Keine Verwechslung zwischen Index / Iterator und Elementen
  • Verwendung einer Reihe klar verwandter Variablen (Artikelliste, Zeiger und Index), die die vielen Fallstricke generischer (vager) Namen wie "count", "index" vermeiden.
  • Präfixe reduzieren die Eingabe (kürzer und funktionieren besser mit automatischer Vervollständigung) als Alternativen wie "itemIndex" und "itemPtr".

Ein weiterer großartiger Punkt bei "iName" -Iteratoren ist, dass ich niemals ein Array mit dem falschen Index indiziere. Wenn ich eine Schleife in eine andere Schleife kopiere, muss ich keine der Schleifenindexvariablen umgestalten.

Vergleichen Sie dieses unrealistisch einfache Beispiel:

for (int i = 0; i < 100; i++)
    for (int j = 0; j < 5; j++)
        list[i].score += other[j].score;

(was schwer zu lesen ist und oft zur Verwendung von "i" führt, wo "j" beabsichtigt war)

mit:

for (int iCompany = 0; iCompany < numCompanies; iCompany++)
    for (int iUser = 0; iUser < numUsers; iUser++)
       companyList[iCompany].score += userList[iUser].score;

(Dies ist viel besser lesbar und beseitigt alle Verwirrung über die Indizierung. Mit der automatischen Vervollständigung in modernen IDEs ist dies auch schnell und einfach einzugeben.)

Der nächste Vorteil ist, dass für Code-Snippets kein Kontext verstanden werden muss. Ich kann zwei Codezeilen in eine E-Mail oder ein Dokument kopieren, und jeder, der dieses Snippet liest, kann den Unterschied zwischen allen Elementen, Konstanten, Zeigern, Indizes usw. erkennen. Ich muss nicht "oh, und sei vorsichtig, weil" hinzufügen 'data' ist ein Zeiger auf einen Zeiger ", weil er 'ppData' heißt.

Und aus dem gleichen Grund muss ich meine Augen nicht aus einer Codezeile heraus bewegen, um sie zu verstehen. Ich muss den Code nicht durchsuchen, um festzustellen, ob 'data' ein Lokal, ein Parameter, ein Mitglied oder eine Konstante ist. Ich muss meine Hand nicht zur Maus bewegen, damit ich den Mauszeiger über 'Daten' bewegen und dann warten kann, bis ein Tooltip (der manchmal nie angezeigt wird) angezeigt wird. So können Programmierer den Code erheblich schneller lesen und verstehen , da sie keine Zeit damit verschwenden, nach oben und unten zu suchen oder zu warten.

(Wenn Sie nicht glauben, dass Sie Zeit damit verschwenden, nach oben und unten zu suchen, um etwas herauszufinden, suchen Sie nach Code, den Sie vor einem Jahr geschrieben und seitdem nicht mehr angesehen haben. Öffnen Sie die Datei und springen Sie etwa auf halber Strecke, ohne sie zu lesen. Sehen Sie, wie Bis jetzt können Sie von diesem Punkt aus lesen, bevor Sie nicht wissen, ob etwas ein Mitglied, ein Parameter oder ein lokales Element ist. Springen Sie jetzt zu einem anderen zufälligen Ort ... Dies tun wir alle den ganzen Tag, wenn wir den Code eines anderen einzeln durchlaufen oder versuchen zu verstehen, wie man ihre Funktion aufruft)

Das Präfix 'm' vermeidet auch die (IMHO) hässliche und wortreiche "this->" - Notation und die damit verbundene Inkonsistenz (selbst wenn Sie vorsichtig sind, erhalten Sie normalerweise eine Mischung aus 'this-> data' und 'Daten' in derselben Klasse, da nichts eine konsistente Schreibweise des Namens erzwingt).

'diese' Notation soll Mehrdeutigkeiten auflösen - aber warum sollte jemand absichtlich Code schreiben, der mehrdeutig sein kann? Mehrdeutigkeit führt früher oder später zu einem Fehler. In einigen Sprachen kann "dies" nicht für statische Elemente verwendet werden. Daher müssen Sie "Sonderfälle" in Ihren Codierungsstil einfügen. Ich bevorzuge eine einzige einfache Codierungsregel, die überall gilt - explizit, eindeutig und konsistent.

Der letzte große Vorteil ist Intellisense und die automatische Vervollständigung. Versuchen Sie, Intellisense in einem Windows Form zu verwenden, um ein Ereignis zu finden. Sie müssen durch Hunderte mysteriöser Basisklassenmethoden scrollen, die Sie niemals aufrufen müssen, um die Ereignisse zu finden. Wenn jedoch jedes Ereignis ein "e" -Präfix hätte, würden sie automatisch in einer Gruppe unter "e" aufgeführt. Das Präfixieren dient daher dazu, die Mitglieder, Konstanten, Ereignisse usw. in der Intellisense-Liste zu gruppieren, wodurch es viel schneller und einfacher wird, die gewünschten Namen zu finden. (Normalerweise kann eine Methode etwa 20-50 Werte (Lokale, Parameter, Mitglieder, Konstanten, Ereignisse) haben, auf die in ihrem Bereich zugegriffen werden kann. Nach der Eingabe des Präfixes (ich möchte jetzt einen Index verwenden, gebe ich also 'i' ein. .. '), mir werden nur 2-5 Optionen für die automatische Vervollständigung angezeigt. Die' zusätzliche Eingabe '

Ich bin ein fauler Programmierer, und die obige Konvention erspart mir viel Arbeit. Ich kann schneller codieren und mache weit weniger Fehler, weil ich weiß, wie jede Variable verwendet werden soll.


Argumente gegen

Also, was sind die Nachteile? Typische Argumente gegen Präfixe sind:

  • "Präfixschemata sind schlecht / böse" . Ich bin damit einverstanden, dass "m_lpsz" und seine Art schlecht durchdacht und völlig nutzlos sind. Aus diesem Grund würde ich empfehlen, eine gut gestaltete Notation zu verwenden, die Ihre Anforderungen unterstützt, anstatt etwas zu kopieren, das für Ihren Kontext ungeeignet ist. (Verwenden Sie das richtige Werkzeug für den Job).

  • "Wenn ich die Verwendung von etwas ändere, muss ich es umbenennen" . Ja, natürlich tun Sie das. Darum geht es beim Refactoring und darum, warum IDEs über Refactoring-Tools verfügen, um diese Aufgabe schnell und schmerzlos zu erledigen. Auch ohne Präfixe, die Verwendung eines variablen Veränderung bedeutet fast sicher sein Name sollte geändert werden.

  • "Präfixe verwirren mich nur" . Wie jedes Werkzeug, bis Sie lernen, wie man es benutzt. Sobald sich Ihr Gehirn an die Namensmuster gewöhnt hat, filtert es die Informationen automatisch heraus und es macht Ihnen nichts aus, dass die Präfixe nicht mehr vorhanden sind. Aber Sie müssen ein oder zwei Wochen lang ein solches Schema anwenden, bevor Sie wirklich "fließend" werden. Und dann schauen sich viele Leute alten Code an und fragen sich, wie sie es jemals ohne ein gutes Präfixschema geschafft haben.

  • "Ich kann mir nur den Code ansehen, um dieses Zeug herauszuarbeiten" . Ja, aber Sie müssen keine Zeit damit verschwenden, an anderer Stelle im Code nachzuschauen oder sich an jedes Detail zu erinnern, wenn die Antwort genau an der Stelle ist, auf die Ihr Auge bereits fokussiert ist.

  • (Einige) dieser Informationen können gefunden werden, indem Sie nur darauf warten, dass ein Tooltip in meiner Variablen angezeigt wird . Ja. Wenn dies unterstützt wird, können Sie bei einigen Präfixtypen, wenn Ihr Code sauber kompiliert wurde, nach einer Wartezeit eine Beschreibung durchlesen und die Informationen finden, die das Präfix sofort übermittelt hätte. Ich bin der Meinung, dass das Präfix ein einfacherer, zuverlässigerer und effizienterer Ansatz ist.

  • "Es ist mehr tippen" . "Ja wirklich?" Ein ganzer Charakter mehr? Oder ist es das? Mit IDE-Tools zur automatischen Vervollständigung wird die Eingabe häufig reduziert, da jedes Präfixzeichen den Suchraum erheblich einschränkt. Drücken Sie "e" und die drei Ereignisse in Ihrer Klasse werden in Intellisense angezeigt. Drücken Sie "c" und die fünf Konstanten werden aufgelistet.

  • "Ich kann this->anstelle von verwenden m" . Ja, das kannst du. Aber das ist nur ein viel hässlicheres und ausführlicheres Präfix! Nur es birgt ein weitaus größeres Risiko (insbesondere in Teams), da es für den Compiler optional ist und daher seine Verwendung häufig inkonsistent ist. mAuf der anderen Seite ist es kurz, klar, explizit und nicht optional, so dass es viel schwieriger ist, Fehler damit zu machen.

Jason Williams
quelle
6
Ich möchte gelesen haben, dass das Problem mit der Hungarien-Notation nur darauf zurückzuführen ist, dass Simonyi missverstanden wurde. Er schrieb, dass ein Präfix verwendet werden sollte, um den Typ einer Variablen anzugeben, wobei er "Typ" meinte, wie in "Art von Dingen", nicht wörtlichen Datentyp. Später haben die Plattform-Leute von Microsoft es aufgegriffen und sich lpsz ausgedacht ... und der Rest ist Geschichte ...
VoidPointer
19
"s is for static" klingt für mich ziemlich nach der "schlechten" Form des Ungarischen.
Jalf
6
@Mehrdad: Ich denke nicht, dass zdies in einer Sprache wie C ++ sehr oft nützlich ist, in der diese Art von Implementierungsdetails auf niedriger Ebene in einer Klasse gekapselt werden sollten, aber in C (wo Nullterminierung eine wichtige Unterscheidung ist) stimme ich Ihnen zu. IMO sollte jedes von uns verwendete Schema nach Bedarf an unsere eigenen Bedürfnisse angepasst werden. Wenn sich die Nullterminierung auf Ihre Verwendung der Variablen auswirkt, ist es nichts Falsches, "z" als nützliches Präfix zu deklarieren.
Jason Williams
14
The most important case is "p" for pointer (because the usage changes from var. to var-> and you have to be much more careful with pointers.Ich bin von ganzem Herzen anderer Meinung. Wenn ich einen falschen Zeiger verwende, wird er einfach nicht kompiliert ( void*kann jedoch eine Ausnahme für Doppelzeiger sein). Und das ganze ->über .genug ist , um mir zu sagen , es ist ein Zeiger. Wenn Sie die automatische Vervollständigung verwenden, verfügt Ihr Editor wahrscheinlich über Deklarationstooltips, sodass keine Präfixe für Variableninformationen erforderlich sind. Egal, gute Antwort.
Thomas Eding
5
Für die klaren, umfassenden und interessanten Erklärungen positiv bewertet, gibt es hier jedoch wenig Anhaltspunkte dafür, wie dies Zeit in C ++ spart. NOCH wird in vielen anderen Sprachen weitgehend nicht verwendet.
115

Ich verwende im Allgemeinen kein Präfix für Mitgliedsvariablen.

Ich habe früher ein mPräfix verwendet, bis jemand darauf hinwies, dass "C ++ bereits ein Standardpräfix für den Mitgliederzugriff hat : this->.

Das ist es, was ich jetzt benutze. Das heißt, wenn es Mehrdeutigkeiten gibt , füge ich das this->Präfix hinzu, aber normalerweise besteht keine Mehrdeutigkeit, und ich kann einfach direkt auf den Variablennamen verweisen.

Für mich ist das das Beste aus beiden Welten. Ich habe ein Präfix, das ich verwenden kann, wenn ich es brauche, und ich kann es nach Möglichkeit weglassen.

Der offensichtliche Gegenpol dazu ist natürlich "Ja, aber dann können Sie nicht auf einen Blick sehen, ob eine Variable ein Klassenmitglied ist oder nicht".

Zu dem sage ich "na und? Wenn Sie das wissen müssen, hat Ihre Klasse wahrscheinlich zu viel Status. Oder die Funktion ist zu groß und kompliziert".

In der Praxis habe ich festgestellt, dass dies sehr gut funktioniert. Als zusätzlichen Bonus kann ich eine lokale Variable einfach einem Klassenmitglied (oder umgekehrt) empfehlen, ohne sie umbenennen zu müssen.

Und das Beste ist, es ist konsequent! Ich muss nichts Besonderes tun oder mich an Konventionen erinnern, um die Konsistenz aufrechtzuerhalten.


Übrigens sollten Sie für Ihre Klassenmitglieder keine führenden Unterstriche verwenden. Sie kommen unangenehm nahe an Namen heran, die von der Implementierung reserviert werden.

Der Standard reserviert alle Namen, beginnend mit doppeltem Unterstrich oder Unterstrich, gefolgt von Großbuchstaben. Außerdem werden alle Namen reserviert, die mit einem einzelnen Unterstrich im globalen Namespace beginnen .

Ein Klassenmitglied mit einem führenden Unterstrich gefolgt von einem Kleinbuchstaben ist also legal, aber früher oder spät werden Sie dasselbe mit einem Bezeichner tun, der mit Großbuchstaben beginnt, oder auf andere Weise gegen eine der oben genannten Regeln verstoßen.

Es ist also einfacher, führende Unterstriche zu vermeiden. Verwenden Sie einen Postfix-Unterstrich oder ein m_oder nur ein mPräfix, wenn Sie den Bereich im Variablennamen codieren möchten.

jalf
quelle
"Ein Klassenmitglied mit einem führenden Unterstrich, gefolgt von einem Kleinbuchstaben, ist also legal, aber früher oder spät werden Sie dasselbe mit einem Bezeichner tun, der mit Großbuchstaben beginnt, oder auf andere Weise gegen eine der oben genannten Regeln verstoßen." - Klassenmitgliedsvariablen befinden sich nicht im globalen Namespace, daher ist ein führender Unterstrich sicher, unabhängig davon, ob auf einen Klein- oder Großbuchstaben folgt.
Mbarnett
3
@mbarnett: Nein, Unterstrich gefolgt von Großbuchstaben ist im Allgemeinen reserviert , nicht nur im globalen Namespace.
Jalf
9
überrascht, dass die Stimme dieser Antwort geringer ist als das Präfix.
Marson Mao
Ich bin mit dieser Antwort einverstanden. Verwenden this->Sie sie nur, wenn Sie angeben müssen, dass es sich um eine Mitgliedsvariable handelt, oder nicht, das ist auch gut.
David Morton
Darüber hinaus müssen Sie Ihre Konvention nicht dokumentieren, um Ihren Code anderen Personen zu geben. Jeder versteht was this->bedeutet.
Caduchon
34

Ich bevorzuge Postfix-Unterstriche wie diesen:

class Foo
{
   private:
      int bar_;

   public:
      int bar() { return bar_; }
};
jkeys
quelle
Ich auch. Ich gebe auch Accessoren / Mutatoren den gleichen Namen.
Rob
4
Interessant. Sieht zunächst etwas hässlich aus, aber ich kann sehen, wie nützlich es sein kann.
ya23
6
Ich würde sagen, es ist viel weniger hässlich als: "mBar" oder "m_bar".
Sydney
6
aber dann hast du vector<int> v_;und das Schreiben v_.push_back(5)ist auch ziemlich hässlich
avim
4
Das ist Google C ++ - Stil.
Justme0
20

In letzter Zeit habe ich eher das Präfix m_ bevorzugt, als überhaupt kein Präfix zu haben. Die Gründe sind nicht so sehr, dass es wichtig ist, Mitgliedsvariablen zu kennzeichnen, sondern dass es Mehrdeutigkeiten vermeidet, sagen wir, Sie haben Code wie:

void set_foo(int foo) { foo = foo; }

Das aus gutem Grund funktioniert nicht, nur einer ist fooerlaubt. Sie haben also folgende Möglichkeiten:

  • this->foo = foo;

    Ich mag es nicht, da es Parameter-Shadowing verursacht, können Sie keine g++ -WshadowWarnungen mehr verwenden , es ist dann auch länger zu tippen m_. Sie stoßen auch immer noch auf Namenskonflikte zwischen Variablen und Funktionen, wenn Sie a int foo;und a haben int foo();.

  • foo = foo_; oder foo = arg_foo;

    Ich benutze das schon eine Weile, aber es macht die Argumentlisten hässlich. Die Dokumentation sollte sich nicht mit der Eindeutigkeit von Namen in der Implementierung befassen. Auch hier bestehen Namenskonflikte zwischen Variablen und Funktionen.

  • m_foo = foo;

    Die API-Dokumentation bleibt sauber, Sie erhalten keine Mehrdeutigkeit zwischen Elementfunktionen und Variablen und die Eingabe ist dann kürzer this->. Der einzige Nachteil ist, dass es POD-Strukturen hässlich macht, aber da POD-Strukturen überhaupt nicht unter der Namensmehrdeutigkeit leiden, muss man sie nicht mit ihnen verwenden. Ein eindeutiges Präfix erleichtert auch einige Such- und Ersetzungsvorgänge.

  • foo_ = foo;

    Die meisten Vorteile m_gelten, aber ich lehne es aus ästhetischen Gründen ab. Ein nachfolgender oder führender Unterstrich lässt die Variable nur unvollständig und unausgeglichen aussehen. m_sieht einfach besser aus. Die Verwendung m_ist auch erweiterbarer, da Sie sie g_für globale und s_statische Elemente verwenden können .

PS: Der Grund, warum Sie m_in Python oder Ruby nicht sehen, ist, dass beide Sprachen ihr eigenes Präfix erzwingen, das Ruby @für Mitgliedsvariablen verwendet und Python benötigt self..

Grumbel
quelle
1
Um fair zu sein, haben Sie mindestens zwei andere Optionen verpasst, z. B. (a) verwenden Sie vollständige Namen wie foonur für Mitglieder und verwenden Sie stattdessen Einzelbuchstaben oder Kurznamen für Parameter oder andere Einheimische / Auswege, wie z int f. oder (b) den Parametern oder anderen Einheimischen etwas voranstellen . guter Punkt bezüglich m_und Hülsen; Ich bin unabhängig voneinander zu der Präferenz gekommen, diese beiden Richtlinien größtenteils zu befolgen.
underscore_d
12

Beim Lesen einer Mitgliedsfunktion ist es unbedingt erforderlich zu wissen, wem jede Variable "gehört", um die Bedeutung der Variablen zu verstehen. In einer Funktion wie dieser:

void Foo::bar( int apples )
{
    int bananas = apples + grapes;
    melons = grapes * bananas;
    spuds += melons;
}

... es ist leicht zu erkennen, woher Äpfel und Bananen kommen, aber was ist mit Trauben, Melonen und Spuds? Sollen wir im globalen Namespace suchen? In der Klassenerklärung? Ist die Variable ein Mitglied dieses Objekts oder ein Mitglied der Klasse dieses Objekts? Ohne die Antwort auf diese Fragen zu kennen, können Sie den Code nicht verstehen. Und in einer längeren Funktion können sogar die Deklarationen lokaler Variablen wie Äpfel und Bananen im Shuffle verloren gehen.

Das Voranstellen einer konsistenten Bezeichnung für Globale, Elementvariablen und statische Elementvariablen (möglicherweise g_, m_ bzw. s_) verdeutlicht sofort die Situation.

void Foo::bar( int apples )
{
    int bananas = apples + g_grapes;
    m_melons = g_grapes * bananas;
    s_spuds += m_melons;
}

Diese mögen zunächst etwas gewöhnungsbedürftig sein - aber was macht die Programmierung dann nicht? Es gab einen Tag, an dem sogar {und} für dich komisch aussahen. Und sobald Sie sich an sie gewöhnt haben, helfen sie Ihnen, den Code viel schneller zu verstehen.

(Die Verwendung von "this->" anstelle von m_ ist sinnvoll, aber noch langwieriger und visuell störender. Ich sehe es nicht als gute Alternative, um alle Verwendungen von Mitgliedsvariablen zu markieren.)

Ein möglicher Einwand gegen das obige Argument wäre, das Argument auf Typen auszudehnen. Es könnte auch zutreffen, dass die Kenntnis des Typs einer Variablen "für das Verständnis der Bedeutung der Variablen unbedingt erforderlich ist". Wenn dem so ist, warum nicht jedem Variablennamen ein Präfix hinzufügen, das seinen Typ identifiziert? Mit dieser Logik erhalten Sie die ungarische Notation. Aber viele Menschen finden die ungarische Notation mühsam, hässlich und nicht hilfreich.

void Foo::bar( int iApples )
{
    int iBananas = iApples + g_fGrapes;
    m_fMelons = g_fGrapes * iBananas;
    s_dSpuds += m_fMelons;
}

ungarisch tutErzählen Sie uns etwas Neues über den Code. Wir verstehen jetzt, dass die Funktion Foo :: bar () mehrere implizite Casts enthält. Das Problem mit dem Code besteht nun darin, dass der Wert der durch ungarische Präfixe hinzugefügten Informationen im Verhältnis zu den visuellen Kosten gering ist. Das C ++ - Typsystem enthält viele Funktionen, mit denen Typen entweder gut zusammenarbeiten oder eine Compiler-Warnung oder einen Compiler-Fehler auslösen können. Der Compiler hilft uns beim Umgang mit Typen - wir brauchen dazu keine Notation. Wir können leicht genug schließen, dass die Variablen in Foo :: bar () wahrscheinlich numerisch sind, und wenn das alles ist, was wir wissen, ist das gut genug, um ein allgemeines Verständnis der Funktion zu erlangen. Daher ist der Wert, den genauen Typ jeder Variablen zu kennen, relativ gering. Die Hässlichkeit einer Variablen wie "s_dSpuds" (oder auch nur "dSpuds") ist jedoch großartig. So,

OldPeculier
quelle
Danke für die s_ Idee. Scheint sehr nützlich und war mir irgendwie nie in den Sinn gekommen.
Chris Olsen
10

Ich kann nicht sagen, wie weit verbreitet es ist, aber persönlich habe ich meinen Mitgliedsvariablen immer (und immer) 'm' vorangestellt. Z.B:

class Person {
   .... 
   private:
       std::string mName;
};

Es ist die einzige Form des Präfixes, die ich verwende (ich bin sehr anti-ungarisch), aber es hat mir im Laufe der Jahre gute Dienste geleistet. Abgesehen davon verabscheue ich im Allgemeinen die Verwendung von Unterstrichen in Namen (oder irgendwo anders), mache aber eine Ausnahme für Präprozessor-Makronamen, da diese normalerweise alle in Großbuchstaben geschrieben sind.


quelle
5
Das Problem bei der Verwendung von m anstelle von m_ (oder _) besteht darin, dass es bei der aktuellen Mode für Kamelfälle schwierig ist, einige Variablennamen zu lesen.
Martin Beckett
1
@Neil ich bin bei dir @mgb: Ich hasse Namen, die mit '_' beginnen. Es ist nur eine Einladung, dass in Zukunft etwas schief geht.
Martin York
1
@Neil: Welche Konvention verwenden Sie dann, wenn Sie keine Unterstriche und kein Kamelgehäuse verwenden?
Jalf
2
Mein Verständnis war, dass es camelCase ist, was die Verwendung von nur m für Variablen wie 'apData' verwirrend macht - es wird eher zu 'mapData' als zu 'm_apData'. Ich benutze _camelCase für geschützte / private Mitgliedsvariablen, weil es auffällt
Martin Beckett
10
@ MartinBeckett: Sie sollten das ain diesem Szenario groß schreiben - Sie machen es sonst nicht richtig. mApData( mPräfix, dann ist der Variablenname apData).
Platinum Azure
8

Der Hauptgrund für ein Elementpräfix besteht darin, zwischen einer lokalen Elementfunktion und einer gleichnamigen Elementvariablen zu unterscheiden. Dies ist nützlich, wenn Sie Getter mit dem Namen der Sache verwenden.

Erwägen:

class person
{
public:
    person(const std::string& full_name)
        : full_name_(full_name)
    {}

    const std::string& full_name() const { return full_name_; }
private:
    std::string full_name_;
};

Die Mitgliedsvariable konnte in diesem Fall nicht als vollständiger Name bezeichnet werden. Sie müssen die Elementfunktion in get_full_name () umbenennen oder die Elementvariable irgendwie dekorieren.

Wolf
quelle
1
Dies ist der Grund, warum ich ein Präfix habe. Ich denke, es foo.name()ist viel besser lesbar als foo.get_name()meiner Meinung nach.
Terrabits
6

Ich glaube nicht, dass eine Syntax einen echten Wert gegenüber einer anderen hat. Wie Sie bereits erwähnt haben, läuft alles auf die Einheitlichkeit der Quelldateien hinaus.

Der einzige Punkt, an dem ich solche Regeln interessant finde, ist, wenn ich zwei Dinge brauche, die als identisch bezeichnet werden, zum Beispiel:

void myFunc(int index){
  this->index = index;
}

void myFunc(int index){
  m_index = index;
}

Ich benutze es, um die beiden zu unterscheiden. Auch wenn ich Aufrufe wie aus der Windows-DLL einschließe, wird RecvPacket (...) aus der DLL möglicherweise in RecvPacket (...) in meinem Code eingeschlossen. In diesen besonderen Fällen kann die Verwendung eines Präfixes wie "_" dazu führen, dass die beiden gleich aussehen. Sie können leicht erkennen, welches welches ist, unterscheiden sich jedoch für den Compiler

Eric
quelle
6

Einige Antworten konzentrieren sich eher auf das Refactoring als auf Namenskonventionen, um die Lesbarkeit zu verbessern. Ich habe nicht das Gefühl, dass einer den anderen ersetzen kann.

Ich kenne Programmierer, denen es unangenehm ist, lokale Deklarationen zu verwenden. Sie ziehen es vor, alle Deklarationen oben in einem Block zu platzieren (wie in C), damit sie wissen, wo sie zu finden sind. Ich habe festgestellt, dass das Deklarieren von Variablen dort, wo sie zuerst verwendet werden, die Zeit verringert, die ich damit verbringe, nach hinten zu schauen, um die Deklarationen zu finden, wenn das Scoping dies zulässt. (Dies gilt für mich auch für kleine Funktionen.) Dadurch kann ich den Code, den ich betrachte, leichter verstehen.

Ich hoffe, es ist klar genug, wie dies mit den Namenskonventionen für Mitglieder zusammenhängt: Wenn Mitgliedern ein einheitliches Präfix vorangestellt wird, muss ich nie zurückblicken. Ich weiß, dass die Deklaration nicht einmal in der Quelldatei gefunden wird.

Ich bin mir sicher, dass ich diese Stile nicht bevorzugt habe. Im Laufe der Zeit habe ich in Umgebungen gearbeitet, in denen sie konsequent eingesetzt wurden, und mein Denken optimiert, um sie zu nutzen. Ich denke, es ist möglich, dass viele Leute, die sich derzeit mit ihnen unwohl fühlen, sie auch bevorzugen, wenn sie konsequent verwendet werden.

Dan Breslau
quelle
5

Diese Konventionen sind genau das. Die meisten Geschäfte verwenden Code-Konventionen, um die Lesbarkeit des Codes zu verbessern, sodass jeder leicht einen Code betrachten und schnell zwischen öffentlichen und privaten Mitgliedern unterscheiden kann.

Mr. Will
quelle
"zwischen Dingen wie öffentlichen und privaten Mitgliedern" - wie häufig ist das wirklich? Ich erinnere mich nicht, es gesehen zu haben, aber andererseits gehe ich nicht herum, um Codebasen oder irgendetwas zu überprüfen.
underscore_d
Ich mache es nicht in meiner eigenen Codierung, aber ich habe an Orten gearbeitet, an denen wir es basierend auf ihren Code-Konventionshandbüchern tun mussten. Ich ziehe es vor, dies nicht zu tun, da fast alle IDEs private Variablen in einer anderen Farbe anzeigen.
Herr Will
Hmm, ich denke es passiert nur in anderen Situationen als in meinen. Normalerweise verwende ich entweder classalle Mitglieder, die private/ sind protected, oder PODs, structderen Variablen alle sind public(und oft auch const). Ich muss mich also nie über die Zugriffsebene eines bestimmten Mitglieds wundern.
underscore_d
5

Andere versuchen, die Verwendung von this-> member zu erzwingen, wenn eine Mitgliedsvariable verwendet wird

Das liegt normalerweise daran, dass es kein Präfix gibt . Der Compiler benötigt genügend Informationen, um die betreffende Variable aufzulösen, sei es ein eindeutiger Name aufgrund des Präfixes oder über das thisSchlüsselwort.

Also, ja, ich denke Präfixe sind immer noch nützlich. Zum einen würde ich lieber '_' eingeben, um auf ein Mitglied zuzugreifen, als 'this->'.

Kent Boogaart
quelle
3
Der Compiler kann es sowieso auflösen ... Lokale Variablen verbergen in den meisten Sprachen diejenigen in einem höheren Bereich. Es ist zum (zweifelhaften) Vorteil der Menschen, die den Code lesen. Jede anständige IDE wird Einheimische / Mitglieder / Globale auf unterschiedliche Weise hervorheben, so dass solche Dinge nicht benötigt werden
rmeador
1
Genau. Einheimische verstecken Klassenmitglieder. Stellen Sie sich einen Konstruktor vor, der diese Elemente festlegt. Normalerweise ist es sinnvoll, die Parameter genauso zu benennen wie die Mitglieder.
Kent Boogaart
6
Warum ist das ein Code-Geruch? Ich würde sagen, es ist durchaus üblich und vernünftig, besonders wenn es um Konstrukteure geht.
Kent Boogaart
3
Ein Konstruktor sollte (im Allgemeinen) Einheimische in seine Initialisierungsliste setzen. Und dort schatten Parameter keine Schattenfeldnamen, aber beide sind zugänglich - so können Sie schreibenstruct Foo { int x; Foo(int x) : x(x) { ... } };
Pavel
2
Ich gehe davon aus, dass das Problem damit auftritt, wenn Sie es Foo(int x, bool blee) : x(x) { if (blee) x += bleecount; } // oops, forgot this->vorziehen, meine Mitgliedsvariablen als nützlich zu bezeichnen und dann Konstruktorparameter anzugeben, die mit den abgekürzten Namen übereinstimmen:Foo(int f) : foo(f) {...}
Steve Jessop
4

Andere Sprachen verwenden Codierungskonventionen, sie sind lediglich unterschiedlich. C # zum Beispiel hat wahrscheinlich zwei verschiedene Stile, die normalerweise verwendet werden, entweder eine der C ++ - Methoden (_variable, mVariable oder ein anderes Präfix wie die ungarische Notation) oder das, was ich als StyleCop-Methode bezeichne.

private int privateMember;
public int PublicMember;

public int Function(int parameter)
{
  // StyleCop enforces using this. for class members.
  this.privateMember = parameter;
}

Am Ende wird es das, was die Leute wissen und was am besten aussieht. Ich persönlich denke, dass Code ohne ungarische Notation besser lesbar ist, aber es kann einfacher werden, eine Variable mit Intellisense zu finden, wenn beispielsweise die ungarische Notation angehängt ist.

In meinem obigen Beispiel benötigen Sie kein m-Präfix für Mitgliedsvariablen, da Sie Ihrer Verwendung dieses Präfix voranstellen. zeigt dasselbe in einer vom Compiler erzwungenen Methode an.

Dies bedeutet nicht unbedingt, dass die anderen Methoden schlecht sind. Die Leute halten sich an das, was funktioniert.

Will Eddins
quelle
3

Wenn Sie eine große Methode oder Codeblöcke haben, ist es praktisch, sofort zu wissen, ob Sie eine lokale Variable oder ein Mitglied verwenden. Es geht darum, Fehler zu vermeiden und die Übersichtlichkeit zu verbessern!

Matthieu
quelle
3
Wenn Sie eine große Methode haben, brechen Sie sie zur besseren Übersichtlichkeit auf.
sbi
4
Es gibt viele Gründe, einige große Methoden nicht zu brechen. Wenn Ihre Methode beispielsweise viel lokalen Status beibehalten muss, müssen Sie entweder viele Parameter an Ihre untergeordneten Methoden übergeben, neue Klassen erstellen, die ausschließlich zum Übergeben von Daten zwischen diesen Methoden vorhanden sind, oder die Statusdaten als deklarieren Mitgliedsdaten der übergeordneten Klasse. Alle diese haben Probleme, die die Klarheit oder Wartbarkeit der Methode im Vergleich zu einer einzelnen langwierigen Methode (insbesondere einer Methode, deren Logik einfach ist) beeinträchtigen würden.
Steve Broberg
3
@sbi: Richtlinien sind genau das; Richtlinien, keine Regeln. Manchmal benötigen Sie große Methoden, die sich logischerweise nicht trennen lassen, und manchmal kollidieren Parameternamen mit Mitgliedern.
Ed S.
Bitte machen Sie Ihre Mitgliedsvariablen nicht öffentlich. Verwenden Sie einfach Accessoren. Die Klammern sollten dem Leser mitteilen, dass es sich um eine Mitgliedsvariable handelt.
Schlüssel
Beachten Sie, dass es in gcc eine Warnung gibt (> = 4.6), um einen Konflikt von Namen zu erkennen:-Wshadow
Caduchon
3

IMO, das ist persönlich. Ich setze überhaupt keine Präfixe. Wenn Code öffentlich sein soll, sollte er meiner Meinung nach besser einige Präfixe haben, damit er besser lesbar ist.

Oft verwenden große Unternehmen ihre eigenen sogenannten "Entwicklerregeln".
Übrigens war der lustigste und klügste, den ich gesehen habe, DRY KISS (Wiederholen Sie sich nicht. Halten Sie es einfach, dumm). :-)

Andrejs Cainikovs
quelle
3

Wie andere bereits gesagt haben, ist es wichtig, umgangssprachlich zu sein (Namensstile und Konventionen an die Codebasis anzupassen, in der Sie schreiben) und konsistent zu sein.

Ich habe jahrelang an einer großen Codebasis gearbeitet, die sowohl die "this->" -Konvention als auch ein Postfix verwendet Unterstrichnotation für Mitgliedsvariablen verwendet. Im Laufe der Jahre habe ich auch an kleineren Projekten gearbeitet, von denen einige keine Konvention zum Benennen von Mitgliedsvariablen hatten und andere unterschiedliche Konventionen zum Benennen von Mitgliedsvariablen hatten. Von diesen kleineren Projekten habe ich immer wieder festgestellt, dass diejenigen, denen eine Konvention fehlte, am schwierigsten sind, schnell darauf zuzugreifen und sie zu verstehen.

Ich bin sehr anal-zurückhaltend in Bezug auf die Benennung. Ich werde mich über den Namen quälen, der einer Klasse oder Variablen zugeordnet werden soll, bis zu dem Punkt, dass ich, wenn ich nichts finden kann, was ich für "gut" halte, ihn als unsinnig bezeichnen und einen Kommentar abgeben werde, der beschreibt, was er wirklich ist ist. Auf diese Weise bedeutet zumindest der Name genau das, was ich beabsichtige - nicht mehr und nicht weniger. Und oft, nachdem ich es für eine Weile benutzt habe, entdecke ich, wie der Name wirklich sein sollte und kann zurückgehen und ihn entsprechend modifizieren oder umgestalten.

Ein letzter Punkt zum Thema einer IDE, die die Arbeit erledigt - das ist alles schön und gut, aber IDEs sind oft nicht in Umgebungen verfügbar, in denen ich die dringendste Arbeit ausgeführt habe. Manchmal ist zu diesem Zeitpunkt nur eine Kopie von 'vi' verfügbar. Außerdem habe ich viele Fälle gesehen, in denen die Vervollständigung des IDE-Codes Dummheit verbreitet hat, wie z. B. falsche Schreibweise in Namen. Daher möchte ich mich lieber nicht auf eine IDE-Krücke verlassen.

Chris Cleeland
quelle
3

Die ursprüngliche Idee für Präfixe in C ++ - Mitgliedsvariablen bestand darin, zusätzliche Typinformationen zu speichern, von denen der Compiler nichts wusste. So könnten Sie beispielsweise eine Zeichenfolge haben, die eine feste Länge von Zeichen hat, und eine andere, die variabel ist und mit einem '\ 0' abgeschlossen wird. Für den Compiler sind sie beide char *, aber wenn Sie versuchen, von einem zum anderen zu kopieren, bekommen Sie große Probleme. Also, aus dem Kopf,

char *aszFred = "Hi I'm a null-terminated string";
char *arrWilma = {'O', 'o', 'p', 's'};

Dabei bedeutet "asz", dass diese Variable "ascii string (nullterminiert) ist, und" arr "bedeutet, dass diese Variable ein Zeichenarray ist.

Dann passiert die Magie. Der Compiler wird mit dieser Aussage vollkommen zufrieden sein:

strcpy(arrWilma, aszFred);

Aber Sie als Mensch können es sich ansehen und sagen: "Hey, diese Variablen sind nicht wirklich vom selben Typ, das kann ich nicht."

Leider verwenden viele Orte Standards wie "m_" für Mitgliedsvariablen, "i" für Ganzzahlen, egal wie verwendet, "cp" für Zeichenzeiger. Mit anderen Worten, sie duplizieren das, was der Compiler weiß, und machen den Code gleichzeitig schwer lesbar. Ich glaube, diese schädliche Praxis sollte gesetzlich verboten und mit harten Strafen belegt werden.

Schließlich gibt es zwei Punkte, die ich erwähnen sollte:

  • Durch den umsichtigen Einsatz von C ++ - Funktionen kann der Compiler die Informationen ermitteln, die Sie in Rohvariablen im C-Stil codieren mussten. Sie können Klassen erstellen, die nur gültige Operationen zulassen. Dies sollte so weit wie möglich geschehen.
  • Wenn Ihre Codeblöcke so lang sind, dass Sie vergessen, welchen Typ eine Variable hat, bevor Sie sie verwenden, sind sie viel zu lang. Verwenden Sie keine Namen, organisieren Sie sich neu.
AL Flanagan
quelle
Präfixe, die den Typ oder die Art der Variablen angeben, sind ebenfalls eine Diskussion wert, aber ich bezog mich hauptsächlich auf Präfixe, die angeben, ob etwas ein (privates) Mitglied / Feld ist. Die von Ihnen erwähnte umgekehrte ungarische Notation kann sehr praktisch sein, wenn sie intelligent angewendet wird (wie in Ihrem Beispiel). Mein Lieblingsbeispiel, wo es Sinn macht, sind relative und absolute Koordinaten. Wenn Sie absX = relX sehen, können Sie deutlich erkennen, dass möglicherweise etwas nicht stimmt. Sie können Funktionen auch entsprechend benennen: absX = absFromRel (relX, offset);
VoidPointer
Hinweis: Die Initialisierung von aszFred ist fraglich (bietet nicht konstanten Zugriff auf eine Literalzeichenfolge), und die Initialisierung von arrWilma wird nicht einmal kompiliert. (Sie wollten wahrscheinlich arrWilma als Array anstelle eines Zeigers deklarieren!) Kein Problem, da Sie geschrieben haben, dass es nur über Ihren Kopf geht ... :-)
Niels Dekker
Ups, du hast absolut recht. Kinder, versuchen Sie das nicht zu Hause. Gehen Sie dazu folgendermaßen vor: 'const char * aszFred = "Hallo, ich bin eine nullterminierte Zeichenfolge"; char arrWilma [] = {'O', 'o', 'p', 's'}; '
AL Flanagan
3

Unser Projekt hat immer "its" als Präfix für Mitgliedsdaten und "the" als Präfix für Parameter verwendet, ohne Präfix für Einheimische. Es ist ein wenig niedlich, aber es wurde von den frühen Entwicklern unseres Systems übernommen, weil sie es als Konvention von einigen kommerziellen Quellbibliotheken sahen, die wir zu dieser Zeit verwendeten (entweder XVT oder RogueWave - vielleicht beide). Sie würden also so etwas bekommen:

void
MyClass::SetName(const RWCString &theName)
{
   itsName = theName;
}

Der Hauptgrund, den ich für das Scoping von Präfixen sehe (und keine anderen - ich hasse die ungarische Notation), ist, dass Sie dadurch nicht in Schwierigkeiten geraten, indem Sie Code schreiben, in dem Sie glauben, auf eine Variable zu verweisen, aber wirklich auf eine andere Variable mit demselben Namen, der im lokalen Bereich definiert ist. Es wird auch das Problem vermieden, Variablennamen zu erstellen, die dasselbe Konzept darstellen, jedoch unterschiedliche Bereiche haben, wie im obigen Beispiel. In diesem Fall müssten Sie sich ohnehin ein Präfix oder einen anderen Namen für den Parameter "theName" einfallen lassen - warum nicht eine konsistente Regel erstellen, die überall gilt?

Nur dies zu verwenden-> ist nicht wirklich gut genug - wir sind nicht so sehr daran interessiert, Mehrdeutigkeiten zu reduzieren wie an Codierungsfehlern, und das Maskieren von Namen mit Bezeichnern mit lokalem Gültigkeitsbereich kann schmerzhaft sein. Zugegeben, einige Compiler haben möglicherweise die Möglichkeit, Warnungen für Fälle auszulösen, in denen Sie den Namen in einem größeren Bereich maskiert haben. Diese Warnungen können jedoch zu einem Ärgernis werden, wenn Sie mit einer großen Anzahl von Bibliotheken von Drittanbietern arbeiten, die zufällig ausgewählt wurden Namen für nicht verwendete Variablen, die gelegentlich mit Ihren eigenen kollidieren.

Was das Selbst angeht - ich finde es ehrlich gesagt einfacher zu tippen als Unterstriche (als Touch-Typist vermeide ich Unterstriche, wann immer dies möglich ist - zu viel Ausdehnung der Ausgangsreihen), und ich finde es lesbarer als einen mysteriösen Unterstrich.

Steve Broberg
quelle
Dies ist die intuitivste Lösung mit der schnellsten Lernkurve, die ich je gehört habe. Ich wünschte, gesprochene Sprachen wären flexibler, um all diese zu handhaben, damit wir nicht über neue Techniken nachdenken müssten, um Unklarheiten im Code zu lösen.
Guney
2

Ich benutze es, weil Intellisense von VC ++ nicht sagen kann, wann private Mitglieder beim Zugriff außerhalb der Klasse angezeigt werden sollen. Die einzige Anzeige ist ein kleines "Schlosssymbol" auf dem Feldsymbol in der Intellisense-Liste. Es macht es einfach einfacher, private Mitglieder (Felder) zu identifizieren. Auch eine Gewohnheit von C #, um ehrlich zu sein.

class Person {
   std::string m_Name;
public:
   std::string Name() { return m_Name; }
   void SetName(std::string name) { m_Name = name; }
};

int main() {
  Person *p = new Person();
  p->Name(); // valid
  p->m_Name; // invalid, compiler throws error. but intellisense doesn't know this..
  return 1;
}
Zack
quelle
2

Ich denke, wenn Sie Präfixe benötigen, um Klassenmitglieder von Elementfunktionsparametern und lokalen Variablen zu unterscheiden, ist entweder die Funktion zu groß oder die Variablen sind schlecht benannt. Wenn es nicht auf den Bildschirm passt, sodass Sie leicht sehen können, was was ist, refactor.

Angesichts der Tatsache, dass sie häufig weit entfernt von ihrem Verwendungsort deklariert werden, finde ich, dass Namenskonventionen für globale Konstanten (und globale Variablen, obwohl IMO diese selten verwenden muss) sinnvoll sind. Aber sonst sehe ich nicht viel Bedarf.

Trotzdem habe ich am Ende aller privaten Klassenmitglieder einen Unterstrich gesetzt. Da alle meine Daten privat sind, bedeutet dies, dass Mitglieder einen nachgestellten Unterstrich haben. Normalerweise mache ich das nicht mehr in neuen Codebasen, aber da Sie als Programmierer meistens mit altem Code arbeiten, mache ich das immer noch viel. Ich bin mir nicht sicher, ob meine Toleranz für diese Gewohnheit darauf zurückzuführen ist, dass ich dies immer getan habe und es immer noch regelmäßig mache oder ob es wirklich sinnvoller ist als das Markieren von Mitgliedsvariablen.

sbi
quelle
2
Dies spiegelt sehr mein Gefühl zu diesem Thema wider. Der Code sollte lesbar sein, ohne auf Präfixe zurückzugreifen. Vielleicht sehen wir in moderneren Sprachen nicht so viele Präfixverwendungen, weil ihre Benutzergemeinschaften die Lesbarkeit ein bisschen mehr als das, was Sie manchmal in C ++ sehen, angenommen haben. Natürlich kann und sollte C ++ lesbar sein. Es ist nur so, dass im Laufe der Jahre viel unlesbares C ++ geschrieben wurde.
VoidPointer
2

In Python werden führende doppelte Unterstriche verwendet, um private Mitglieder zu emulieren. Weitere Details finden Sie in dieser Antwort

Konstantin Tenzin
quelle
1

Aufgrund der Speicherverwaltung ist es nützlich, zwischen Mitgliedsvariablen und lokalen Variablen zu unterscheiden. Im Allgemeinen sollten Heap-zugewiesene Mitgliedsvariablen im Destruktor zerstört werden, während Heap-zugewiesene lokale Variablen in diesem Bereich zerstört werden sollten. Das Anwenden einer Namenskonvention auf Mitgliedsvariablen erleichtert die korrekte Speicherverwaltung.

Frankster
quelle
Wie das? Der Destruktor hat keinen Zugriff auf lokale Variablen, die in anderen Funktionen deklariert wurden, sodass hier kein Raum für Verwirrung besteht. Außerdem sollten vom Heap zugewiesene lokale Variablen nicht existieren . Und Heap-zugewiesene Mitgliedsvariablen sollten so ziemlich nur innerhalb von RAII-Klassen existieren.
Jalf
"Heap-zugewiesene lokale Variablen sollten nicht existieren" ist ein bisschen stark. Wenn Sie sie jedoch verwenden, ist es äußerst wichtig, sicherzustellen, dass sie ordnungsgemäß freigegeben werden. Daher hilft eine disziplinierte Namenskonvention für Mitglieder im Vergleich zu lokalen Variablen unermesslich dabei, dies sicherzustellen.
Frankster
1

Code Complete empfiehlt m_varname für Mitgliedsvariablen.

Obwohl ich die m_-Notation nie für nützlich gehalten hätte, würde ich McConnells Meinung beim Aufbau eines Standards Gewicht verleihen.

Paul Nathan
quelle
2
Nicht, wenn er nicht erklärt, warum der Unterstrich. Ich bin ein großer Fan seines Buches "Rapid Development", das ich hier mehrfach empfohlen habe, aber viel weniger von "Code Complete" (was ich zugeben werde, dass ich es seit seiner Veröffentlichung nicht mehr gelesen habe).
1

Ich verwende fast nie Präfixe vor meinen Variablennamen. Wenn Sie eine ausreichend anständige IDE verwenden, sollten Sie in der Lage sein, Referenzen leicht umzugestalten und zu finden. Ich verwende sehr klare Namen und habe keine Angst vor langen Variablennamen. Ich hatte auch mit dieser Philosophie nie Probleme mit dem Umfang.

Das einzige Mal, wenn ich ein Präfix verwende, ist in der Signaturzeile. Ich werde einer Methode Parameter mit _ voranstellen, damit ich sie defensiv programmieren kann.

James
quelle
1

Sie sollten niemals ein solches Präfix benötigen. Wenn ein solches Präfix Ihnen einen Vorteil bietet, muss Ihr Codierungsstil im Allgemeinen korrigiert werden, und es ist nicht das Präfix, das verhindert, dass Ihr Code klar ist. Typische falsche Variablennamen sind "andere" oder "2". Sie beheben dies nicht, indem Sie verlangen, dass es sich um eine andere handelt, sondern indem Sie den Entwickler dazu bringen, darüber nachzudenken, was diese Variable dort im Kontext dieser Funktion tut. Vielleicht meinte er remoteSide oder newValue oder secondTestListener oder etwas in diesem Bereich.

Es ist ein effektiver Anachronismus, der immer noch zu weit verbreitet ist. Hören Sie auf, Ihren Variablen ein Präfix voranzustellen, und geben Sie ihnen Eigennamen, deren Klarheit widerspiegelt, wie lange sie verwendet werden. Bis zu 5 Zeilen können Sie ohne Verwirrung "i" nennen. Über 50 Zeilen hinaus benötigen Sie einen ziemlich langen Namen.

dascandy
quelle
1

Ich mag es, wenn Variablennamen den darin enthaltenen Werten nur eine Bedeutung geben und aus dem Namen herauslassen, wie sie deklariert / implementiert werden. Ich möchte wissen, was der Wert bedeutet, Punkt. Vielleicht habe ich mehr als durchschnittlich Refactoring durchgeführt, aber ich finde, dass das Einbetten, wie etwas in den Namen implementiert wird, das Refactoring mühsamer macht, als es sein muss. Präfixe, die angeben, wo oder wie Objektmitglieder deklariert werden, sind implementierungsspezifisch.

color = Red;

Die meiste Zeit ist es mir egal, ob Rot eine Aufzählung, eine Struktur oder was auch immer ist, und wenn die Funktion so groß ist, dass ich mich nicht erinnern kann, ob Farbe lokal deklariert wurde oder ein Mitglied ist, ist es wahrscheinlich Zeit zu brechen die Funktion in kleinere logische Einheiten.

Wenn Ihre zyklomatische Komplexität so groß ist, dass Sie ohne implementierungsspezifische Hinweise, die in die Namen der Dinge eingebettet sind, nicht verfolgen können, was im Code vor sich geht, müssen Sie höchstwahrscheinlich die Komplexität Ihrer Funktion / Methode reduzieren.

Meistens verwende ich 'this' nur in Konstruktoren und Initialisierern.

ChrisG65
quelle
0

Ich benutze m_ für Mitgliedsvariablen, nur um Intellisense und die damit verbundene IDE-Funktionalität zu nutzen. Wenn ich die Implementierung einer Klasse codiere, kann ich m_ eingeben und die Combobox mit allen m_-Mitgliedern sehen, die zusammen gruppiert sind.

Aber ich könnte natürlich ohne Probleme leben. Es ist nur mein Arbeitsstil.

Hernán
quelle
Sie könnten auch eingebenthis->
Toast
0

Laut C ++ CODING STANDARDS VON JOINT STRIKE FIGHTER AIR VEHICLE (Dezember 2005):

AV-Regel 67

Öffentliche und geschützte Daten sollten nur in Strukturen verwendet werden, nicht in Klassen. Begründung: Eine Klasse kann ihre Invariante beibehalten, indem sie den Zugriff auf ihre Daten kontrolliert. Eine Klasse kann jedoch den Zugriff auf ihre Mitglieder nicht kontrollieren, wenn diese Mitglieder nicht privat sind. Daher sollten alle Daten in einer Klasse privat sein.

Daher wird das Präfix "m" nicht mehr verwendet, da alle Daten privat sein sollten.

Es ist jedoch eine gute Angewohnheit, das Präfix p vor einem Zeiger zu verwenden, da es sich um eine gefährliche Variable handelt.

Pierre-Louis Deschamps
quelle
0

Viele dieser Konventionen stammen aus einer Zeit ohne anspruchsvolle Redakteure. Ich würde empfehlen, eine geeignete IDE zu verwenden, mit der Sie jede Art von Variable einfärben können. Farbe ist bei weitem leichter zu erkennen als jedes Präfix.

Wenn Sie noch mehr Details zu einer Variablen benötigen, sollte jede moderne IDE diese Ihnen anzeigen können, indem Sie das Caret oder den Cursor darüber bewegen. Und wenn Sie eine Variable falsch verwenden (zum Beispiel einen Zeiger mit dem Operator.), Wird ohnehin eine Fehlermeldung angezeigt.

Elias Müller
quelle