Wie soll ich die Mitglieder einer C ++ - Klasse bestellen?

73

Ist es besser, alle privaten Mitglieder zu haben, als alle geschützten, dann alle öffentlichen? Oder umgekehrt? Oder sollte es mehrere private, geschützte und öffentliche Labels geben, damit die Vorgänge von den Konstruktoren getrennt werden können und so weiter? Welche Punkte sollte ich bei dieser Entscheidung berücksichtigen?

Tommy Herbert
quelle
10
Es ist interessant zu sehen, wie solche fast ausschließlich meinungsbasierten Themen "früher" herzlich aufgenommen wurden, während ich davon ausgehe und eher hoffe, dass sie heutzutage in Vergessenheit geraten.
underscore_d
1
Ich bin jetzt geneigt, dem zuzustimmen, aber ich zögere, es zu löschen, weil es sehr beliebt war.
Tommy Herbert
@underscore_d Ich frage mich wirklich, warum viele, viele Fragen, wenige Wörter, die keine Details oder Klarheit haben und vollständig auf Meinungen basieren, 700 positive Stimmen haben. Während die gleiche Frage heute 3-4 Abstimmungen erhalten und sofort innerhalb von Sekunden geschlossen werden würde
Aryan Parekh
1
Fortschritt, denke ich?
Tommy Herbert
1
Diese Frage werden sich alle C ++ - Programmierer irgendwann stellen. Und es ist nicht klar, dass die Antwort lautet: "Es basiert auf Meinungen". Ich denke, solche Fragen sollten leben dürfen, solange der Ton höflich bleibt und solange noch subtile Punkte zu machen sind, zum Beispiel über Lesbarkeit oder Wartbarkeit.
Elling

Antworten:

55

Ich habe die öffentliche Schnittstelle an die erste Stelle gesetzt, aber das habe ich nicht immer getan. Ich habe Dinge rückwärts gemacht, mit privat, dann geschützt, dann öffentlich. Rückblickend ergab das wenig Sinn.

Als Entwickler einer Klasse kennen Sie wahrscheinlich die "Innereien", aber die Benutzer der Klasse kümmern sich nicht viel darum oder sollten es zumindest nicht. Sie interessieren sich hauptsächlich dafür, was die Klasse für sie tun kann, oder?

Also stelle ich die Öffentlichkeit an die erste Stelle und organisiere sie normalerweise nach Funktion / Dienstprogramm. Ich möchte nicht, dass sie durch meine Benutzeroberfläche waten müssen, um alle Methoden in Bezug auf X zu finden. Ich möchte, dass sie all diese Dinge auf organisierte Weise zusammen sehen.

Ich benutze niemals mehrere öffentliche / geschützte / private Bereiche - zu verwirrend, um meiner Meinung nach zu folgen.

itsmatt
quelle
1
Dies ist nur meine Meinung, aber ich stimme nicht zu, dass sich die Benutzer einer Klasse nicht um die Innereien kümmern sollten. Ich denke, die Benutzer sollten sich darum kümmern, da die Abstraktion und Kapselung nur dazu dient, komplexe Probleme anzugehen und die Benutzer nicht von der Notwendigkeit zu befreien, sich mit den Details zu befassen.
Dave Van den Eynde
9
Schätzen Sie den Kommentar, Dave. Wenn ich die Effizienz oder das "Wie" einer Klasse bewerte oder wenn ich mir Sorgen mache, dass es nicht korrekt ist, dann werde ich mich um die Innereien kümmern, aber hauptsächlich als Benutzer der Klasse, ich ' Ich mache mir Sorgen um das Verhalten der Klasse und nicht darum, wie sie die Dinge intern verwaltet.
matt
1
@itsmatt sizeof (Objekt) hängt von der Reihenfolge der Mitglieder ab, wenn ich mich nicht irre. Wenn ja, hat dies keine Auswirkungen auf die Bestellung? Angenommen, ich habe ein Double sowohl in privaten als auch in öffentlichen und dann in anderen Variablen vom Typ char. Muss ich doppelte Variablen zusammenfügen? Wie gehen wir in diesem Fall um?
Rajesh
Dies ist vollständig meinungsbasiert mit Argumenten für beide Seiten. Wenn man mit beginnt, privatekann man aus versteckten Datenelementen aufbauen, was Clients der Klasse tun können - eine Art logische Reihenfolge . Wenn Sie mit beginnen, publickann jemand, der die Klasse liest, sofort die öffentliche Oberfläche sehen und mit dem Lesen aufhören, sobald der öffentliche Abschnitt endet - eine Art Reihenfolge, die für den Leser einfacher ist .
Petr Vepřek
41

Google bevorzugt diese Reihenfolge : "Typedefs und Enums, Konstanten, Konstruktoren, Destruktor, Methoden, einschließlich statischer Methoden, Datenelemente, einschließlich statischer Datenelemente."

Matthew Wilson (Safari-Abonnement erforderlich) empfiehlt die folgende Reihenfolge: "Konstruktion, Betrieb, Attribute, Iteration, Status, Implementierung, Mitglieder und mein Favorit, Nicht implementiert werden."

Sie bieten gute Gründe, und diese Art von Ansatz scheint ziemlich normal zu sein, aber was auch immer Sie tun, seien Sie konsequent.

Josh Kelley
quelle
9

Es ist meine Meinung und ich würde wetten, dass die meisten Leute zustimmen würden, dass öffentliche Methoden an erster Stelle stehen sollten. Eines der Grundprinzipien von OO ist, dass Sie sich nicht um die Implementierung kümmern müssen. Ein Blick auf die öffentlichen Methoden sollte Ihnen alles sagen, was Sie wissen müssen, um die Klasse zu verwenden.

korona
quelle
IMO, OOP-Prinzipien haben nichts damit zu tun, wo Sie Ihre öffentlichen Mitglieder schreiben.
Vassilis
7

Der Codierungsstil ist eine Quelle für überraschend hitzige Gespräche. Vor diesem Hintergrund riskiere ich eine andere Meinung:

Code sollte so geschrieben werden, dass er für Menschen am besten lesbar ist. Ich stimme dieser Aussage, die hier mehrmals abgegeben wurde, voll und ganz zu.

Die Abweichung ist, um welche Rolle es sich handelt.

Um dem Benutzer der Klasse zu helfen, die Verwendung zu verstehen, sollte eine ordnungsgemäße Dokumentation geschrieben und gepflegt werden . Ein Benutzer sollte niemals den Quellcode lesen müssen, um die Klasse verwenden zu können. Wenn dies erfolgt (entweder manuell oder mithilfe von In-Source-Dokumentationstools), spielt die Reihenfolge, in der öffentliche und private Klassenmitglieder in der Quelle definiert sind, für den Benutzer keine Rolle.

Für jemanden, der den Code verstehen muss , während der Codeüberprüfung, der Pull-Anforderung oder der Wartung, ist die Reihenfolge jedoch sehr wichtig - die Regel ist einfach:

Elemente sollten definiert werden, bevor sie verwendet werden

Dies ist weder eine Compiler-Regel noch eine streng öffentliche oder private Regel, sondern eine Regel des gesunden Menschenverstandes - der menschlichen Lesbarkeit. Wir lesen Code nacheinander und wenn wir jedes Mal, wenn wir ein Klassenmitglied sehen, "jonglieren" müssen, aber beispielsweise seinen Typ nicht kennen, wirkt sich dies nachteilig auf die Lesbarkeit des Codes aus.

Eine strikte Trennung zwischen privat und öffentlich verstößt gegen diese Regel, da private Klassenmitglieder angezeigt werden, nachdem sie in einer öffentlichen Methode verwendet wurden.

PolarBear2015
quelle
1
Wenn ich könnte, würde ich mehr als nur diese eine Stimme geben, die ich geben darf. Ich hätte es nicht besser sagen können. Verwenden Sie Ihre IDEs Registerkarte „Struktur“ oder in der Dokumentation , wenn Sie wollen konsumieren , wenn Sie tatsächlich benötigen , um Kritik / verstehen den Code dieses wie hier dargestellt am meisten Sinn macht.
Scravy
5

Wie immer schreiben Sie zuerst Ihren Code für Menschen . Betrachten Sie die Person, die Ihre Klasse verwenden und legen Sie die wichtigsten Mitglieder / Aufzählungen / typedefs / was auch immer , um sie an der Spitze.

Normalerweise bedeutet dies, dass öffentliche Mitglieder an der Spitze stehen, da dies die meisten Verbraucher Ihrer Klasse am meisten interessiert. Geschützt folgt, gefolgt von Privaten. Meistens.

Es gibt einige Ausnahmen.

Gelegentlich ist die Initialisierungsreihenfolge wichtig und manchmal muss eine private vor einer öffentlichen deklariert werden. Manchmal ist es wichtiger, dass eine Klasse geerbt und erweitert wird. In diesem Fall können die geschützten Mitglieder höher platziert werden. Und wenn Unit-Tests auf Legacy-Code gehackt werden, ist es manchmal einfacher, öffentliche Methoden verfügbar zu machen. Wenn ich diese Beinahe-Sünde begehen muss, platziere ich diese am Ende der Klassendefinition.

Aber es sind relativ seltene Situationen.

Ich finde, dass "öffentlich, geschützt, privat" für Verbraucher Ihrer Klasse meistens am nützlichsten ist. Es ist eine anständige Grundregel, sich daran zu halten.

Es geht jedoch weniger um die Bestellung per Zugang als vielmehr um die Bestellung nach Interesse des Verbrauchers .

MattyT
quelle
4

Normalerweise definiere ich zuerst die Schnittstelle (zu lesen), die öffentlich, dann geschützt und dann privat ist. In vielen Fällen gehe ich jetzt einen Schritt vorwärts und verwende (wenn ich damit umgehen kann) das PIMPL-Muster, um alle privaten Dinge vollständig vor der Schnittstelle der realen Klasse zu verbergen.

class Example1 {
public:
   void publicOperation();
private:
   void privateOperation1_();
   void privateOperation2_();

   Type1 data1_;
   Type2 data2_;
};
// example 2 header:
class Example2 {
   class Impl;
public:
   void publicOperation();
private:
   std::auto_ptr<Example2Impl> impl_;
};
// example2 cpp:
class Example2::Impl
{
public:
   void privateOperation1();
   void privateOperation2();
private: // or public if Example2 needs access, or private + friendship:
   Type1 data1_;
   Type2 data2_;
};

Sie können feststellen, dass ich private (und auch geschützte) Mitglieder mit einem Unterstrich postfixiere. Die PIMPL-Version verfügt über eine interne Klasse, für die die Außenwelt die Operationen nicht einmal sieht. Dadurch bleibt die Klassenschnittstelle vollständig sauber: Es wird nur die reale Schnittstelle angezeigt. Keine Notwendigkeit, über Ordnung zu streiten.

Während der Klassenkonstruktion fallen Kosten an, da ein dynamisch zugewiesenes Objekt erstellt werden muss. Dies funktioniert auch sehr gut für Klassen, die nicht erweitert werden sollen, aber einige Mängel bei Hierarchien aufweisen. Geschützte Methoden müssen Teil der externen Klasse sein, damit Sie sie nicht wirklich in die interne Klasse übertragen können.

David Rodríguez - Dribeas
quelle
2

Ich denke, es geht nur um Lesbarkeit.

Einige Leute gruppieren sie gerne in einer festen Reihenfolge, sodass Sie beim Öffnen einer Klassendeklaration schnell wissen, wo Sie suchen müssen, z. B. nach öffentlichen Datenmitgliedern.

Im Allgemeinen denke ich, dass die wichtigsten Dinge an erster Stelle stehen sollten. Für 99,6% aller Klassen bedeutet dies ungefähr die öffentlichen Methoden und insbesondere den Konstruktor. Dann kommen ggf. öffentliche Datenelemente (denken Sie daran: Kapselung ist eine gute Idee), gefolgt von geschützten und / oder privaten Methoden und Datenelementen.

Dies ist etwas, das möglicherweise von den Codierungsstandards großer Projekte abgedeckt wird. Es kann eine gute Idee sein, dies zu überprüfen.

entspannen
quelle
2

In unserem Projekt ordnen wir die Mitglieder nicht nach Zugriff, sondern nach Verwendung. Und damit meine ich, wir bestellen die Mitglieder so, wie sie verwendet werden. Wenn ein öffentliches Mitglied ein privates Mitglied in derselben Klasse verwendet, befindet sich dieses private Mitglied normalerweise irgendwo vor dem öffentlichen Mitglied, wie im folgenden (vereinfachenden) Beispiel:

class Foo
{
private:
  int bar;

public:
  int GetBar() const
  {
    return bar;
  }
};

Hier ist das Element bar angeordnet ist , bevor das Element GetBar () , da die erstere von der letzteren verwendet. Dies kann zu mehreren Zugriffsabschnitten führen, wie im folgenden Beispiel:

class Foo
{
public:
  typedef int bar_type;

private:
  bar_type bar;

public:
  bar_type GetBar() const
  {
    return bar;
  }
};

Der bar_type Mitglied wird vom bar- Mitglied verwendet, siehe?

Warum ist das? Ich weiß nicht, es schien natürlicher, dass Sie, wenn Sie irgendwo in der Implementierung auf ein Mitglied stoßen und mehr Details dazu benötigen (und IntelliSense wieder vermasselt ist), es irgendwo oben von Ihrem Arbeitsplatz aus finden können.

Dave Van den Eynde
quelle
2

In der Praxis spielt es selten eine Rolle. Es ist in erster Linie eine Frage der persönlichen Präferenz.

Es ist sehr beliebt, öffentliche Methoden an die erste Stelle zu setzen, angeblich, damit Benutzer der Klasse sie leichter finden können. Header sollten jedoch niemals Ihre Hauptdokumentationsquelle sein. Wenn Sie also "Best Practices" auf die Idee stützen, dass Benutzer Ihre Header betrachten, scheint dies für mich die Marke zu verfehlen.

Es ist wahrscheinlicher, dass sich Personen in Ihren Headern befinden, wenn sie die Klasse ändern . In diesem Fall sind sie es sollten sie sich um die private Schnittstelle kümmern.

Was auch immer Sie wählen, machen Sie Ihre Header sauber und einfach zu lesen. Das Wichtigste ist, dass ich leicht alle Informationen finden kann, nach denen ich gerade suche, egal ob ich ein Benutzer der Klasse oder ein Betreuer der Klasse bin.

Dan Olson
quelle
1

Es ist sehr hilfreich für die Leute, die Ihre Klasse verwenden, um zuerst die öffentliche Schnittstelle aufzulisten. Es ist der Teil, den sie interessieren und den sie verwenden können. Geschützt und privat können nach folgen.

Innerhalb der öffentlichen Oberfläche ist es praktisch, Konstruktoren, Eigenschaftszugriffs- und Mutatoren sowie Operatoren in unterschiedlichen Gruppen zu gruppieren.

Todd
quelle
1

Beachten Sie, dass Sie (abhängig von Ihrem Compiler und Ihrem dynamischen Linker) die Kompatibilität mit früheren Versionen einer gemeinsam genutzten Bibliothek beibehalten können, indem Sie nur das Ende der Klasse (dh das Ende der Schnittstelle) hinzufügen und nichts anderes entfernen oder ändern. (Dies gilt für G ++ und libtool, und das dreiteilige Versionsschema für gemeinsam genutzte GNU / Linux-Bibliotheken spiegelt dies wider.)

Es gibt auch die Idee, dass Sie Mitglieder der Klasse bestellen sollten, um Platzverschwendung aufgrund der Speicherausrichtung zu vermeiden. Eine Strategie besteht darin, Mitglieder von der kleinsten zur größten Größe zu bestellen. Ich habe das weder in C ++ noch in C gemacht.

Reed Hedges
quelle
Ich glaube, die Empfehlung lautet tatsächlich vom größten zum kleinsten, nicht wahr? Ich müsste noch einmal nachsehen, vielleicht können wir eine Referenz finden.
Dan Olson
Hier ist eine gute Antwort für Reihenfolge und Ausrichtung.
Jacek Sieka
1

Insgesamt sollte Ihre öffentliche Benutzeroberfläche vor allem stehen, denn das ist die Haupt- / Einzelsache, an der Benutzer Ihrer Klassen interessiert sein sollten. (In der Realität gilt dies natürlich nicht immer, aber es ist ein guter Anfang.)

Innerhalb dieses Bereichs sind Elementtypen und Konstanten zuerst am besten, gefolgt von Konstruktionsoperatoren, Operationen und dann Elementvariablen.

dcw
quelle
1

Stellen Sie die privaten Felder an die erste Stelle.

Bei modernen IDEs lesen die Benutzer die Klasse nicht, um herauszufinden, um welche öffentliche Schnittstelle es sich handelt.

Sie verwenden dafür nur Intelligenz (oder einen Klassenbrowser).

Wenn jemand die Klassendefinition durchliest, liegt dies normalerweise daran, dass er verstehen möchte, wie es funktioniert.

In diesem Fall hilft es am meisten, die Felder zu kennen. Es zeigt Ihnen, was die Teile des Objekts sind.

Scott Wisniewski
quelle
Ich kann mit Qualifikationen einverstanden sein, aber irgendwie verstehe ich, warum dies abgelehnt wurde ... nicht, dass die Wähler das Problem erklären wollten. Qualifikationen : Wenn ich Klassen für meine Verwendung codiere, mache ich normalerweise Folgendes: private Felder oben, dann private Funktionen; dann öffentliches Zeug unten. Offensichtlich für Bestellabhängigkeiten anpassen. Und ich benutze nicht einmal eine IDE, nur vim! Aber es gibt das Problem : Würde ich Klassen für andere schreiben, würde ich für sie schreiben, dh mit den wichtigsten Dingen im Voraus. Das ist nur höflich, besonders wenn sie auch vermeiden, welche IDE gerade in Mode ist
underscore_d
Ich habe eine Klasse gelesen, um zu sehen, wie man sie benutzt. Nur wenn die Verwendung nicht klar ist, schaue ich mir die Implementierungsdetails an. Viele Leute verwenden immer noch Vim und Emacs und haben keinen Zugriff auf xwindows, um "moderne" IDEs auszuführen.
Edwinc
-3

Kommt ganz auf deine Vorlieben an. Es gibt keinen "richtigen Weg".

Wenn ich C ++ in meinen eigenen Haustierprojekten mache, halte ich persönlich die Konvention ein, dass ich den Zugriffsmodifikator vor jedes Mitglied oder jede Methodendeklaration setze.

Marko
quelle
Ich habe Sie nicht notiert, aber ich vermute, einige haben es getan, weil es unnötig und übertrieben ist, jedem Mitglied einen Zugriffsmodifikator vorzulegen. Ehrlich gesagt finde ich, dass es die Lesbarkeit aufgrund des zusätzlichen Rauschens beeinträchtigt.
MattyT
2
Dadurch sieht Ihr C ++ wie Java aus. Die Frage ist dann, ob Javas "Geben Sie ein zusätzliches Wort pro Deklaration ein" besser oder schlechter ist als C ++ "Zugriffsspezifizierer ändern den globalen Status, der implizit von jeder Deklaration verwendet wird". Auch, ob Sie es in C ++ auf C ++ tun sollten, auch wenn Sie Java bevorzugen.
Steve Jessop
1
Ich kann nicht verstehen, warum jemand bisher herabgestuft wurde, weil er es gewagt hat, eine Meinung zu haben, als Antwort auf eine Frage, die fast ausschließlich auf Meinungen basiert - ein klar definierter Grund für das Markieren, das, wie ich gerne denke, nicht einmal bekommen hätte in der heutigen vermutlich besser modifizierten SO vom Boden abheben.
underscore_d