it-swarm.com.de

Warum sollten Präfixe für Member-Variablen in C++ - Klassen verwendet werden?

In vielen C++ - Codes werden syntaktische Konventionen zum Markieren von Member-Variablen verwendet. Häufige Beispiele sind

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

Andere versuchen, die Verwendung dieses Elements -> member zu erzwingen, wenn eine Membervariable verwendet wird.

Nach meiner Erfahrung können die meisten größeren Codebasis 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, keine spezielle Auszeichnung für Elementvariablen zu verwenden.

Ist diese Konvention heute noch in C++ nützlich oder ist es nur ein Anachronismus? Zumal es in Bibliotheken so uneinheitlich verwendet wird. Haben nicht die anderen Sprachen gezeigt, die man ohne Member-Präfixe machen kann?

134
VoidPointer

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

_Foo

_L

sind alle reservierte Wörter während

_foo

_l

sind nicht. In anderen Situationen sind führende Unterstriche vor Kleinbuchstaben nicht zulässig. In meinem speziellen Fall habe ich festgestellt, dass _L zufällig von Visual C++ 2005 reserviert wurde, und der Konflikt verursachte unerwartete Ergebnisse.

Ich bin am Zaun, wie nützlich es ist, lokale Variablen zu kennzeichnen.

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

45
Juan

Ich bin alles für Präfixe gut gemacht.

Ich denke, die (System-) ungarische Notation ist für die meisten "schlechten Raps" verantwortlich, die Präfixe bekommen.

Diese Notation ist in stark typisierten Sprachen, z.B. in C++ "lpsz", 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 auf nicht terminierte Zeichenfelder verweisen und es nicht wirklich allzu schwierig ist zu wissen, dass "customerName" eine Zeichenfolge ist!

Ich verwende jedoch Präfixe, um die Verwendung einer Variablen anzugeben (im Wesentlichen "Apps Hungarian"), obwohl ich den Begriff "Hungarian" lieber meide, weil er eine schlechte und unfaire Assoziation mit hat System Ungarisch), und dies ist ein sehr praktischer zeitsparender 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 statisch
  • i für Indizes und Iteratoren
  • e für Events

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 viel vorsichtiger mit Zeigern umgehen müssen - NULLs, Zeigerarithmetik usw.), aber alle anderen sind sehr praktisch.

Sie können beispielsweise denselben Variablennamen auf mehrere Arten in einer einzigen Funktion 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 eines Satzes klar verwandter Variablen (Elementliste, Zeiger und Index), die die zahlreichen 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 wichtiger Punkt von "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;

(Das ist viel besser lesbar und beseitigt alle Unklarheiten bei der Indizierung. Mit der automatischen Vervollständigung in modernen IDEs ist dies auch schnell und einfach zu tippen.)

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 Mitgliedern, Konstanten, Zeigern, Indizes usw. erkennen. Ich muss nicht "oh" hinzufügen und da vorsichtig sein '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 bewegen, um es zu verstehen. Ich muss den Code nicht durchsuchen, um festzustellen, ob es sich bei 'data' um einen lokalen Wert, einen Parameter, ein Mitglied oder eine Konstante handelt. 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, auf und ab zu suchen oder zu warten.

(Wenn Sie nicht glauben, dass Sie Zeit damit verschwenden, nach Aufgaben zu suchen, suchen Sie nach Code, den Sie vor einem Jahr geschrieben haben und den Sie seitdem nicht mehr gelesen haben. Öffnen Sie die Datei und springen Sie ungefähr zur Hälfte Sehen Sie, wie weit Sie von diesem Punkt aus lesen können, bevor Sie nicht wissen, ob es sich um ein Mitglied, einen Parameter oder eine lokale Adresse handelt. Wechseln Sie nun zu einem anderen zufälligen Ort ... Dies tun wir alle den ganzen Tag über, wenn wir es tun sind einzelne Schritte durch den Code eines anderen oder versuchen zu verstehen, wie man seine Funktion aufruft)

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

'diese' Notation soll Mehrdeutigkeit auflösen - aber warum sollte jemand absichtlich Code schreiben, der mehrdeutig sein kann? Mehrdeutigkeit wird früher oder später zu einem Fehler führen. In einigen Sprachen kann 'this' nicht für statische Member verwendet werden, daher müssen Sie 'special cases' in Ihren Codierungsstil einfügen. Ich bevorzuge eine einfache Codierungsregel, die überall gilt - explizit, eindeutig und konsistent.

Der letzte große Vorteil liegt in Intellisense und der automatischen Vervollständigung. Verwenden Sie Intellisense in einem Windows Form, um ein Ereignis zu finden. Sie müssen durch Hunderte mysteriöser Basisklassenmethoden blättern, 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 also dazu, die Mitglieder, Konstanten, Ereignisse usw. in der Intellisense-Liste zu gruppieren, sodass Sie die gewünschten Namen viel schneller und einfacher finden können. (Normalerweise hat eine Methode ungefähr 20-50 Werte (Locals, Parameter, Member, Konstanten, Ereignisse), auf die in ihrem Gültigkeitsbereich zugegriffen werden kann. Nach der Eingabe des Präfixes (ich möchte jetzt einen Index verwenden, also gebe ich 'i ein. .. '), mir werden nur 2 bis 5 Optionen für die automatische Vervollständigung angeboten. Das Attribut "zusätzliche Eingabe" für Präfixe und aussagekräftige Namen reduziert den Suchraum drastisch und beschleunigt die Entwicklungsgeschwindigkeit messbar.)

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


Argumente dagegen

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 Ihren Anforderungen entspricht, 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, darum geht es beim Refactoring und darum, warum IDEs Refactoring-Tools haben, um diese Aufgabe schnell und schmerzlos zu erledigen. Auch ohne Präfixe bedeutet eine Änderung der Verwendung einer Variablen mit ziemlicher Sicherheit, dass der Name geändert werden muss .

  • "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, werden die Informationen automatisch herausgefiltert, und es macht Ihnen nichts aus, dass die Präfixe nicht mehr vorhanden sind. Aber Sie müssen ein oder zwei Wochen solide nach einem solchen Schema vorgehen, bevor Sie wirklich "fließend" werden. Und dann schauen sich viele Leute alten Code an und fragen sich, wie sie jemals ohne ein gutes Präfixschema geschafft haben.

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

  • (Einige) dieser Informationen können gefunden werden, indem Sie nur auf einen Tooltip warten, der in meiner Variablen angezeigt wird. Ja. Wo dies für einige Präfixtypen unterstützt wird, können Sie nach einer Wartezeit beim sauberen Kompilieren Ihres Codes eine Beschreibung durchlesen und die Informationen finden, die das Präfix sofort übermittelt hätte. Ich halte das Präfix für einen einfacheren, zuverlässigeren und effizienteren Ansatz.

  • "Es ist mehr Tippen" . "Ja wirklich?" Ein ganzer Charakter mehr? Oder ist es das? Mit IDE Auto-Vervollständigungstools 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 m" verwenden. Na 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. m ist andererseits kurz, klar, explizit und nicht optional, so dass es viel schwieriger ist, Fehler damit zu machen.

222
Jason Williams

Im Allgemeinen verwende ich kein Präfix für Mitgliedsvariablen.

Ich verwendete ein m-Präfix, bis jemand darauf hinwies, dass "C++ bereits ein Standardpräfix für den Mitgliederzugriff hat: this->.

Das ist was ich jetzt benutze. Das heißt, wenn Mehrdeutigkeit vorliegt, füge ich das Präfix this-> hinzu, aber normalerweise ist keine Mehrdeutigkeit vorhanden, und ich kann 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 Widerspruch dazu ist natürlich "Ja, aber Sie können nicht auf einen Blick erkennen, ob eine Variable ein Klassenmitglied ist oder nicht".

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

In der Praxis habe ich herausgefunden, dass dies extrem gut funktioniert. Als zusätzlicher Bonus kann ich eine lokale Variable leicht zu einem Klassenmitglied (oder umgekehrt) befördern, ohne sie umzubenennen.

Und das Beste ist, dass es konsistent ist! Ich muss nichts Besonderes tun oder mich an Konventionen erinnern, um die Konsistenz zu wahren. 


Übrigens verwenden Sie sollten nicht führende Unterstriche für Ihre Klassenmitglieder. Sie kommen unbequemen Namen nahe, die von der Implementierung reserviert werden.

Der Standard reserviert alle Namen, die mit einem doppelten Unterstrich oder Unterstrich gefolgt von Großbuchstaben beginnen. Es reserviert auch alle Namen, die mit einem einzelnen Unterstrich im globalen Namensraum beginnen.

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

Daher ist es einfacher, Unterstriche zu vermeiden. Verwenden Sie einen Postfix-Unterstrich oder ein m_- oder einfach ein m-Präfix, wenn Sie den Gültigkeitsbereich im Variablennamen kodieren möchten.

106
jalf

Ich bevorzuge Postfix-Unterstriche wie solche:

class Foo
{
   private:
      int bar_;

   public:
      int bar() { return bar_; }
};
32
jkeys

In letzter Zeit tendiere ich dazu, m_-Präfix zu bevorzugen, anstatt überhaupt kein Präfix zu haben. Die Gründe sind weniger wichtig, um Mitgliedsvariablen zu kennzeichnen, aber um Mehrdeutigkeiten zu vermeiden. Angenommen, Sie haben Code wie:

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

Das funktioniert natürlich nicht, nur ein foo ist erlaubt. Ihre Möglichkeiten sind also:

  • this->foo = foo;

    Ich mag es nicht, da es Parameterschatten verursacht. Sie können g++ -Wshadow Warnungen nicht mehr verwenden. Es ist auch länger, als m_ einzugeben. Sie haben auch noch Namenskonflikte zwischen Variablen und Funktionen, wenn Sie einen int foo; und eine int foo(); haben.

  • 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 es ist kürzer, sie als this-> einzugeben. Der einzige Nachteil ist, dass POD-Strukturen dadurch hässlich werden. Da POD-Strukturen jedoch in erster Linie nicht unter der Mehrdeutigkeit von Namen leiden, müssen Sie sie nicht mit ihnen verwenden. Ein eindeutiges Präfix erleichtert auch das Suchen und Ersetzen.

  • foo_ = foo;

    Die meisten Vorteile von m_ gelten, aber ich lehne es aus ästhetischen Gründen ab. Ein abschließender oder führender Unterstrich lässt die Variable nur unvollständig und unausgeglichen aussehen. m_ sieht einfach besser aus. Die Verwendung von m_ ist auch erweiterbarer, da Sie g_ für globale Werte und s_ für statische Werte verwenden können.

PS: Der Grund, warum Sie m_ in Python oder Ruby nicht sehen, ist, dass beide Sprachen das eigene Präfix erzwingen, Ruby verwendet @ für Membervariablen und Python erfordert self..

19
Grumbel

Beim Lesen einer Member-Funktion ist das Wissen, wer jede Variable "besitzt", absolut unerlässlich, 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 genug zu sehen, wo Äpfel und Bananen herkommen, aber wie sieht es mit Trauben, Melonen und Spudeln aus? Sollten wir in den globalen Namensraum schauen? In der Klassendeklaration? 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 selbst die Deklarationen lokaler Variablen wie Äpfel und Bananen im Shuffle verloren gehen.

Das Voranstellen einer konsistenten Beschriftung für Globals, Membervariablen und statische Membervariablen (möglicherweise g_, m_ und s_) macht die Situation sofort klar.

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

Das kann anfangs etwas gewöhnungsbedürftig sein - aber was macht das Programmieren nicht? Es gab einen Tag, an dem sogar Sie komisch aussahen. Wenn Sie sich erst einmal daran gewöhnt haben, können Sie den Code schneller verstehen.

(Die Verwendung von "this->" anstelle von m_ ist sinnvoll, ist jedoch noch umständlicher und visuell störender. Ich sehe es nicht als eine gute Alternative, um alle Verwendungen von Member-Variablen zu kennzeichnen.)

Ein möglicher Einwand gegen das obige Argument wäre die Erweiterung des Arguments auf Typen. Es kann auch richtig sein, dass das Wissen um den Typ einer Variablen "für das Verständnis der Bedeutung der Variablen absolut unerlässlich ist". Wenn dies der Fall ist, können Sie jedem Variablennamen, der seinen Typ identifiziert, ein Präfix hinzufügen. Mit dieser Logik endet die ungarische Schreibweise. Aber viele Menschen finden die ungarische Schreibweise mühsam, hässlich und wenig hilfreich.

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

Ungarisch tut Erzählt uns etwas Neues über den Code. Wir verstehen jetzt, dass es mehrere implizite Besetzungen in der Funktion Foo :: bar () gibt. Das Problem mit dem Code besteht nun darin, dass der Wert der Informationen, die durch ungarische Präfixe hinzugefügt werden, im Verhältnis zu den visuellen Kosten gering ist. Das C++ - Typsystem enthält viele Funktionen, die dazu beitragen, dass Typen entweder gut zusammenarbeiten oder eine Warnung oder einen Fehler beim Compiler auslösen. Der Compiler hilft uns beim Umgang mit Typen - wir brauchen dazu keine Notation. Wir können leicht feststellen, dass die Variablen in Foo :: bar () wahrscheinlich numerisch sind, und wenn dies 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 lehnt eine Kosten-Nutzen-Analyse die ungarische Schreibweise ab, während der Nutzen von g_, s_ und m_ die Kosten in den Augen vieler Programmierer überwältigt.

10
OldPeculier

Ich kann nicht sagen, wie weit verbreitet es ist, aber wenn ich persönlich spreche, habe ich immer (und immer) 'm' meinen Mitgliedsvariablen 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 gut getan. Abgesehen davon verabscheue ich generell die Verwendung von Unterstrichen in Namen (oder anderswo für diese Angelegenheit), mache aber eine Ausnahme für Präprozessormakro-Namen, da diese normalerweise alle in Großbuchstaben geschrieben werden.

10
anon

Der Hauptgrund für ein Member-Präfix besteht darin, zwischen einer lokalen Member-Funktion und einer Member-Variablen mit demselben Namen 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 Membervariable konnte in diesem Fall nicht als full_name bezeichnet werden. Sie müssen die Member-Funktion in get_full_name () umbenennen oder die Member-Variable irgendwie dekorieren.

8

Einige Antworten konzentrieren sich auf das Refactoring und nicht auf die Benennung von Konventionen, um die Lesbarkeit zu verbessern. Ich habe nicht das Gefühl, dass einer den anderen ersetzen kann.

Ich habe Programmierer kennengelernt, denen die Verwendung lokaler Deklarationen unangenehm ist. Sie ziehen es vor, alle Deklarationen (wie in C) oben in einem Block zu platzieren, damit sie wissen, wo sie zu finden sind. Ich habe festgestellt, dass das Definieren von Variablen dort, wo sie zum ersten Mal verwendet werden, die Zeit verringert, die ich für einen Rückblick auf die Deklarationen verbringe. (Dies gilt auch für kleine Funktionen.) Dadurch kann ich den Code, den ich mir anschaue, leichter verstehen.

Ich hoffe, dass es klar genug ist, wie dies mit den Namenskonventionen der Mitglieder zusammenhängt: Wenn Mitglieder einheitlich vorangestellt werden, muss ich niemals 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, als ich in Umgebungen arbeitete, in denen sie konsequent eingesetzt wurden, optimierte ich mein Denken, um sie zu nutzen. Ich denke, es ist möglich, dass viele Leute, die sich derzeit unwohl mit ihnen fühlen, bei konsequentem Gebrauch auch sie bevorzugen würden.

6
Dan Breslau

Andere versuchen, die Verwendung von .__ zu erzwingen. this-> member wenn ein Mitglied Variable wird verwendet

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

Ja, ich denke, Präfixe sind immer noch nützlich. Ich möchte lieber "_" eingeben, um auf ein Mitglied zuzugreifen, als "this->".

6
Kent Boogaart

Ich glaube nicht, dass eine Syntax einen anderen Wert hat. Alles läuft darauf hinaus, wie Sie bereits erwähnt haben, auf die Einheitlichkeit der Quelldateien.

Der einzige Punkt, an dem ich solche Regeln interessant finde, ist, wenn ich zwei identische Dinge brauche, zum Beispiel:

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

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

Ich benutze es, um die beiden zu unterscheiden. Wenn ich Anrufe einwickle, z. B. von Windows-Dll, könnte RecvPacket (...) aus der Dll in RecvPacket (...) in meinem Code eingeschlossen werden. In diesen besonderen Fällen kann die Verwendung eines Präfixes wie "_" dazu führen, dass die beiden gleich aussehen, wobei leicht zu erkennen ist, welches das ist, jedoch für den Compiler unterschiedlich ist

6
Eric

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

5
Mr. Will

Andere Sprachen verwenden Codierungskonventionen, sie unterscheiden sich lediglich. C # hat zum Beispiel wahrscheinlich zwei verschiedene Stile, die von den Benutzern verwendet werden, entweder eine der C++ - Methoden (_variable, mVariable oder ein anderes Präfix wie die ungarische Schreibweise) oder das, was ich als StyleCop-Methode bezeichnet habe.

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 die ungarische Notation besser lesbar ist, aber es wird möglicherweise einfacher, 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 Member-Variablen, da Sie Ihrer Verwendung das Präfix voranstellen. gibt dasselbe in einer vom Compiler erzwungenen Methode an.

Dies bedeutet nicht zwangsläufig, dass die anderen Methoden schlecht sind, die Leute bleiben bei dem, was funktioniert.

4
Will Eddins

Die ursprüngliche Idee für Präfixe für C++ - Member-Variablen bestand darin, zusätzliche Typinformationen zu speichern, über die der Compiler nicht wusste. Sie könnten zum Beispiel eine Zeichenfolge haben, die eine feste Länge von Zeichen hat, und eine andere, die variabel ist und durch ein '\ 0' abgeschlossen wird. Für den Compiler sind sie beide char *, aber wenn Sie versuchen, von einem auf den anderen zu kopieren, bekommen Sie große Probleme. Also aus meinem 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 Zeichenfeld 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 der gleiche Typ, das kann ich nicht".

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

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

  • Durch die sorgfältige Verwendung der C++ - Funktionen kann der Compiler die Informationen kennen, die Sie in unverarbeiteten C-Variablen codieren mussten. Sie können Klassen erstellen, die nur gültige Operationen zulassen. Dies sollte so praktisch wie möglich erfolgen.
  • Wenn Ihre Codeblöcke so lang sind, dass Sie vergessen, welcher Typ eine Variable ist, bevor Sie sie verwenden, sind sie way zu lang. Benutze keine Namen, ordne sie neu an.
3
A. L. Flanagan

Unser Projekt hat immer "sein" als Präfix für Mitgliedsdaten verwendet und "the" als Präfix für Parameter, ohne Präfix für Ortsansässige. Es ist ein bisschen kleinlich, aber es wurde von den frühen Entwicklern unseres Systems übernommen, weil sie sahen, dass es von einigen kommerziellen Quellbibliotheken, die wir damals verwendeten, als Konvention verwendet wurde (entweder XVT oder RogueWave - möglicherweise beide). So würde man so etwas bekommen:

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

Der Hauptgrund, den ich für Scoping-Präfixe sehe (und keine anderen - ich hasse die ungarische Notation) - besteht darin, dass Sie nicht in Schwierigkeiten geraten, indem Sie Code schreiben, bei dem Sie auf eine Variable verweisen, sich aber wirklich auf eine andere Variable beziehen mit demselben Namen, der im lokalen Bereich definiert ist. Dadurch wird auch das Problem vermieden, dass Variablennamen verwendet werden, um dasselbe Konzept darzustellen, jedoch mit unterschiedlichen Gültigkeitsbereichen, wie im obigen Beispiel. In diesem Fall müssten Sie ohnehin ein Präfix oder einen anderen Namen für den Parameter "theName" eingeben - warum nicht eine konsistente Regel, die überall gilt?.

Die Verwendung dieses Befehls> ist nicht wirklich gut genug - wir sind weniger daran interessiert, die Mehrdeutigkeit zu reduzieren, als die Codierungsfehler zu reduzieren, und das Maskieren von Namen mit lokal begrenzten Bezeichnern kann ein Schmerz sein. Einige Compiler haben zwar die Möglichkeit, Warnungen auszulösen, wenn Sie den Namen in einem größeren Bereich maskiert haben. Diese Warnungen können jedoch ein Ärgernis sein, 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 oder die selbst betrifft - ich finde es ehrlich gesagt einfacher zu schreiben als Unterstriche (als Berührungsschreiber vermeide ich, wo immer möglich, Unterstriche - zu viel, um sich von den Reihen abzuheben), und ich finde es lesbarer als einen mysteriösen Unterstrich.

3
Steve Broberg

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.

Seit Jahren arbeite ich an einer großen Codebasis, die sowohl die "this->" - Konvention als auch eine postfix - Unterstreichungsnotation für Elementvariablen verwendet. Im Laufe der Jahre habe ich auch an kleineren Projekten gearbeitet, von denen einige keine Konventionen für die Benennung von Mitgliedsvariablen hatten und andere unterschiedliche Konventionen für die Benennung von Mitgliedsvariablen. Von diesen kleineren Projekten habe ich immer wieder festgestellt, dass es am schwierigsten ist, schnell auf solche zuzugreifen, bei denen es an Konventionen mangelt.

Ich bin sehr anal-zurückhaltend bei der Namensgebung. Ich werde mich mit dem Namen quälen, der einer Klasse oder Variablen zugeordnet werden soll, bis zu dem Punkt, dass ich, wenn ich nicht auf etwas kommen kann, das ich für "gut" halte, beschließe, es unsinnig zu nennen und einen Kommentar abzugeben, der beschreibt, was es wirklich ist ist. Auf diese Weise bedeutet zumindest der Name genau das, was ich damit beabsichtige - nicht mehr und nicht weniger. Und oft, nachdem ich es für eine Weile benutzt habe, entdecke ich, wie der Name sein sollte wirklich und kann zurückgehen und 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 erledigt habe. Manchmal ist zu diesem Zeitpunkt nur eine Kopie von 'vi' verfügbar. Außerdem habe ich viele Fälle gesehen, in denen die IDE - Codevervollständigung Dummheit verbreitet hat, z. B. die falsche Schreibweise von Namen. Daher möchte ich mich nicht auf eine IDE Krücke verlassen müssen.

3
Chris Cleeland

Wenn Sie über große Methoden- oder Codeblöcke verfügen, ist es ratsam, sofort zu wissen, ob Sie eine lokale Variable oder ein Member verwenden. es ist fehler zu vermeiden und für bessere klarheit!

3
Matthieu

IMO, das ist persönlich. Ich setze überhaupt keine Präfixe ein. Wenn Code öffentlich sein soll, denke ich, sollte er besser ein paar Präfixe haben, damit er besser lesbar ist.

Oft verwenden große Unternehmen ihre eigenen "Entwicklerregeln".
Übrigens, der lustigste und zugleich intelligenteste, den ich sah, war DRY KISS. :-)

3

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 haben einen schlechten Namen. Wenn es nicht auf den Bildschirm passt, können Sie leicht sehen, was was ist, umgestalten. 

In Anbetracht der Tatsache, dass sie oft weit von ihrem Verwendungsort entfernt sind, finde ich, dass Namenskonventionen für globale Konstanten (und globale Variablen, obwohl IMO selten notwendig ist, diese zu verwenden) sinnvoll. Aber sonst sehe ich nicht viel Bedarf. 

Das heißt, ich habe am Ende aller Privatklassen einen Unterstrich gesetzt. Da alle meine Daten privat sind, bedeutet dies, dass die 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 nicht sicher, ob meine Toleranz für diese Angewohnheit auf die Tatsache zurückzuführen ist, dass ich dies immer getan habe und immer noch regelmäßig mache oder ob es wirklich sinnvoller ist als die Markierung von Elementvariablen. 

2
sbi

In Python werden führende doppelte Unterstriche verwendet, um private Mitglieder zu emulieren. Für weitere Details siehe diese Antwort

2

Ich verwende es, weil VC++ Intellisense nicht sagen kann, wann private Mitglieder angezeigt werden sollen, wenn auf die Klasse zugegriffen wird. Der einzige Hinweis ist ein kleines "Schloss" -Symbol auf dem Feldsymbol in der Intellisense-Liste. Es macht es nur einfacher, private Mitglieder (Felder) zu identifizieren. Ehrlich gesagt auch eine Angewohnheit von C #.

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;
}
2
Zack

Aufgrund der Speicherverwaltung kann zwischen Mitgliedervariablen und lokalen Variablen unterschieden werden. Im Allgemeinen sollten Heap-zugewiesene Member-Variablen im Destruktor zerstört werden, während Heap-zugewiesene lokale Variablen innerhalb dieses Bereichs zerstört werden sollten. Das Anwenden einer Namenskonvention auf Membervariablen erleichtert die korrekte Speicherverwaltung.

1
frankster

Sie sollten niemals ein solches Präfix benötigen. Wenn ein solches Präfix einen Vorteil bietet, muss der Codierstil im Allgemeinen korrigiert werden, und es ist nicht das Präfix, das den Code daran hindert, klar zu sein. Typische ungültige Variablennamen sind "other" oder "2". Sie können dies nicht beheben, indem Sie es als mOther festlegen. Sie beheben es, indem Sie den Entwickler dazu bringen, darüber nachzudenken, was die 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. Beenden Sie das Präfix Ihrer Variablen und geben Sie ihnen die richtigen Namen, deren Klarheit die Nutzungsdauer widerspiegelt. Bis zu 5 Zeilen könnten Sie ohne Verwirrung "i" nennen. jenseits von 50 Zeilen brauchen Sie einen ziemlich langen Namen. 

1
dascandy

Vor meinen Variablennamen verwende ich fast nie Präfixe. Wenn Sie ein anständiges IDE verwenden, sollten Sie leicht Refactor finden und Referenzen finden können. Ich benutze sehr klare Namen und habe keine Angst vor langen Variablennamen. Ich hatte auch nie Probleme mit dieser Philosophie.

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

1
James

Ich benutze m_ für Membervariablen, nur um Intellisense und die dazugehörige IDE-Funktionalität zu nutzen. Wenn ich die Implementierung einer Klasse programmiere, kann ich m_ eingeben und die Combobox mit allen m_ Mitgliedern sehen, die zusammen gruppiert sind. 

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

1
Hernán

Ich mag Variablennamen, die den Werten, die sie enthalten, nur eine Bedeutung geben und die Art und Weise, wie sie deklariert/implementiert werden, außerhalb des Namens lassen. Ich möchte wissen, was der Wert bedeutet, Punkt. Vielleicht habe ich überdurchschnittlich viel Refactoring durchgeführt, aber ich finde, dass die Einbettung, wie etwas in den Namen implementiert wird, das Refactoring langweiliger macht, als es sein muss. Präfixe, die angeben, wo oder wie Objektmitglieder deklariert werden, sind implementierungsspezifisch.

color = Red;

Meistens ist es mir egal, ob Rot ein Enum, 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 die implementierungsspezifischen Hinweise, die in den Namen von Dingen eingebettet sind, nicht den Überblick über die Vorgänge im Code behalten können, müssen Sie höchstwahrscheinlich die Komplexität Ihrer Funktion/Methode reduzieren.

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

1
ChrisG65

Code Complete empfiehlt m_varname für Member-Variablen.

Ich habe die m_-Notation zwar nie für nützlich gehalten, aber ich würde McConnells Meinung beim Aufbau eines Standards wichtig machen.

1
Paul Nathan

Viele dieser Konventionen stammen aus einer Zeit ohne ausgefeilte Editoren. Ich würde empfehlen, ein richtiges IDE zu verwenden, mit dem 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 es jedes moderne IDE 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.), Erhalten Sie trotzdem eine Fehlermeldung.

0
Elias Mueller

Laut JOINT STRIKE FIGHTER LUFTFAHRZEUG C++ - CODE-STANDARDS (Dezember 2005):

AV-Regel 67

Öffentliche und geschützte Daten sollten nur in .__ verwendet werden. structs - nicht Klassen. Begründung: Eine Klasse kann ihre Invariante durch die Kontrolle des Zugriffs auf seine Daten. Eine Klasse kann jedoch nicht Zugriff auf seine Mitglieder kontrollieren, wenn diese Mitglieder nicht privat sind. Daher alles Daten in einer Klasse sollten privat sein.

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

Es ist jedoch eine gute Angewohnheit, das p-Präfix vor einem Zeiger zu verwenden, da dies eine gefährliche Variable ist.