Googles C ++ Style Guide. Teil 4

Teil 1. Einführung

...

Teil 4. Klassen

...





Dieser Artikel ist eine Übersetzung eines Teils des C ++ - Styleguides von Google ins Russische.

Originalartikel (Gabel auf Github), aktualisierte Übersetzung .





Klassen





Klassen sind der Hauptbaustein in C ++. Und natürlich werden sie oft verwendet. In diesem Abschnitt werden die grundlegenden Regeln und Verbote beschrieben, die bei der Verwendung von Klassen zu beachten sind.



Code im Konstruktor





Rufen Sie im Konstruktor keine virtuellen Methoden auf. Vermeiden Sie eine Initialisierung, die fehlschlagen kann (und es gibt keine Möglichkeit, einen Fehler zu signalisieren. Hinweis: Bitte beachten Sie, dass Google keine Ausnahmen mag).



Definition



Im Allgemeinen kann jede Initialisierung in einem Konstruktor durchgeführt werden (d. H. Alle Initialisierungen können in einem Konstruktor durchgeführt werden).



Pro



  • Sie müssen sich keine Sorgen um nicht initialisierte Klassen machen.
  • Objekte, die im Konstruktor vollständig initialisiert sind, können const sein und sind auch in Standardcontainern und -algorithmen einfacher zu verwenden.




Vs



  • Wenn im Konstruktor virtuelle Funktionen aufgerufen werden, werden Implementierungen aus der abgeleiteten Klasse nicht aufgerufen. Selbst wenn die Klasse jetzt keine Nachkommen mehr hat, könnte dies in Zukunft zu einem Problem werden.
  • ( ) ( ).
  • , ( — ) . : bool IsValid(). .
  • . , , .




Urteilskonstruktoren



dürfen keine virtuellen Funktionen aufrufen. In einigen Fällen (falls zulässig) können Entwurfsfehler durch Programmbeendigung behoben werden. Andernfalls berücksichtigen Sie das Factory-Methodenmuster oder verwenden Sie Init () (weitere Details hier: TotW # 42 ). Verwenden Sie Init () nur, wenn das Objekt über Statusflags verfügt, mit denen bestimmte öffentliche Funktionen aufgerufen werden können (da es schwierig ist, mit einem teilweise erstellten Objekt vollständig zu arbeiten).



Implizite Konvertierungen





Deklarieren Sie keine impliziten Conversions. Verwenden Sie das explizite Schlüsselwort für Typkonvertierungsoperatoren und Konstruktoren mit einzelnen Argumenten.



Definition Mit



impliziten Konvertierungen kann ein Objekt eines Quelltyps verwendet werden, wenn ein anderer Typ (Zieltyp) erwartet wird, z. B. das Übergeben eines Arguments vom Typ int an eine Funktion, die ein Double erwartet .



Zusätzlich zu den impliziten Konvertierungen, die von der Programmiersprache angegeben werden, können Sie auch Ihre eigenen benutzerdefinierten Konvertierungen definieren, indem Sie der Klassendeklaration die entsprechenden Elemente hinzufügen (sowohl Quelle als auch Ziel). Die quellenseitige implizite Konvertierung wird als Operator + Empfängertyp deklariert (z. B. Operator bool () ). Die implizite Konvertierung auf der Empfängerseite wird von einem Konstruktor implementiert, der den Quelltyp als einziges Argument verwendet (zusätzlich zu den Standardargumenten).



Das explizite Schlüsselwort kann auf einen Konstruktor oder einen Konvertierungsoperator angewendet werden, um explizit anzugeben, dass eine Funktion nur verwendet werden kann, wenn eine explizite Typübereinstimmung vorliegt (z. B. nach einer Umwandlung). Dies gilt nicht nur für implizite Konvertierungen, sondern auch für Initialisierungslisten in C ++ 11:



class Foo {
  explicit Foo(int x, double y);
  ...
};
void Func(Foo f);

      
      







Func({42, 3.14});  // 

      
      







Dieses Codebeispiel ist technisch gesehen keine implizite Konvertierung, wird jedoch von der Sprache so behandelt, als ob es explizit wäre .



Pro



  • , .
  • , string_view std::string const char*.
  • .








  • , ( ).
  • , : , .
  • , .
  • explicit : , .
  • , . — , .
  • , , , .




Urteilskonvertierungsoperatoren



und Einzelargumentkonstruktoren müssen mit dem expliziten Schlüsselwort deklariert werden . Es gibt auch eine Ausnahme: Kopier- und Verschiebungskonstruktoren können seitdem ohne explizite Deklaration deklariert werden Sie führen keine Typkonvertierung durch. Bei Wrapper-Klassen für andere Typen können auch implizite Konvertierungen erforderlich sein (fragen Sie in diesem Fall unbedingt Ihre Upstream-Verwaltungsberechtigung, diese wichtige Regel zu ignorieren).



Konstruktoren, die nicht mit einem einzigen Argument aufgerufen werden können, können ohne explizite Deklaration deklariert werden . Konstruktoren, die eine einzelne std :: initializer_list akzeptieren muss auch ohne explizite Deklaration deklariert werden , um die Kopierinitialisierung zu unterstützen (z. B. MyType m = {1, 2}; ).



Kopierbare und verschiebbare Typen





Die öffentliche Schnittstelle der Klasse muss explizit die Fähigkeit angeben, alles zu kopieren und / oder zu verschieben oder umgekehrt, alles zu verbieten. Unterstützen Sie das Kopieren und / oder Verschieben nur, wenn diese Vorgänge für Ihren Typ sinnvoll sind.



Definition Ein verschiebbarer



Typ kann aus temporären Werten initialisiert oder zugewiesen werden.



Kopierbarer Typ - kann von einem anderen Objekt desselben Typs (d. H. Dasselbe wie das verschiebbare) initialisiert oder zugewiesen werden, vorausgesetzt , das ursprüngliche Objekt bleibt unverändert. Beispielsweise ist std :: unique_ptr <int> verschiebbar , jedoch nicht Der Typ, der kopiert werden soll (da sich der Wert des ursprünglichen std :: unique_ptr <int> -Objekts ändern muss, wenn er dem Ziel zugewiesen wird). intund std :: string sind Beispiele für verschiebbare Typen, die auch kopiert werden können: Für int sind die Verschiebungs- und Kopiervorgänge gleich, für std :: string erfordert die Verschiebungsoperation weniger Ressourcen als das Kopieren.



Bei benutzerdefinierten Typen wird das Kopieren vom Kopierkonstruktor und vom Kopieroperator angegeben. Die Bewegung wird entweder vom Verschiebungskonstruktor mit dem Verschiebungsoperator oder (falls nicht vorhanden) von den entsprechenden Kopierfunktionen angegeben.



Kopier- und Verschiebungskonstruktoren können vom Compiler implizit aufgerufen werden, beispielsweise wenn Objekte nach Wert übergeben werden.



Pro



Objekte kopierbaren und verschiebbaren Typs können als Wert übergeben und empfangen werden, wodurch die API einfacher, sicherer und vielseitiger wird. In diesem Fall gibt es keine Probleme mit dem Eigentum an dem Objekt, seinem Lebenszyklus, seiner Wertänderung usw., und es ist auch nicht erforderlich, sie im "Vertrag" anzugeben (all dies ist anders als das Übergeben von Objekten per Zeiger oder Referenz). Eine verzögerte Kommunikation zwischen dem Client und der Implementierung wird ebenfalls verhindert, wodurch der Code für den Compiler viel einfacher zu verstehen, zu warten und zu optimieren ist. Solche Objekte können als Argumente für andere Klassen verwendet werden, für die ein Wert übergeben werden muss (z. B. die meisten Container), und sie sind im Allgemeinen flexibler (z. B. bei Verwendung in Entwurfsmustern).



Kopier- / Verschiebungskonstruktoren und zugehörige Zuweisungsoperatoren sind normalerweise einfacher zu definieren als Alternativen wie Clone () , CopyFrom () oder Swap (), weil Der Compiler kann die erforderlichen Funktionen generieren (implizit oder mit = default ). Sie (Funktionen) sind einfach zu deklarieren und Sie können sicher sein, dass alle Klassenmitglieder kopiert werden. Konstruktoren (Kopieren und Verschieben) sind im Allgemeinen effizienter, weil Keine Speicherzuweisung erforderlich, separate Initialisierung, zusätzliche Zuweisungen, sind gut optimiert (siehe Kopierelision ).



Mit Verschiebungsoperatoren können Sie r-Wert-Ressourcen von Objekten effizient (und implizit) bearbeiten. Dies erleichtert manchmal das Codieren.



Gegen



Einige Typen müssen nicht kopierbar sein, und die Unterstützung für Kopiervorgänge kann kontraintuitiv sein oder zu falschem Verhalten führen. Typen für Singletones ( Registerer ), Objekte zum Bereinigen (z. B. wenn der Gültigkeitsbereich verlassen wird) ( Bereinigen ) oder eindeutige Daten ( Mutex ) enthalten sind in ihrer Bedeutung nicht kopierbar. Kopiervorgänge für Basisklassen mit Nachkommen können auch zum Aufteilen von Objekten führen ... Die Standardkopiervorgänge (oder schlecht geschriebenen) können zu Fehlern führen, die schwer zu erkennen sind.



Kopierkonstruktoren werden implizit aufgerufen, und dies ist leicht zu übersehen (insbesondere für Programmierer, die zuvor in Sprachen geschrieben haben, in denen Objekte als Referenz übergeben werden). Sie können die Leistung auch reduzieren, indem Sie unnötige Kopien erstellen.



Urteil Die



öffentliche Schnittstelle jeder Klasse muss explizit angeben, welche Kopier- und / oder Verschiebevorgänge sie unterstützt. Dies geschieht normalerweise im öffentlichen Bereich in Form expliziter Deklarationen der erforderlichen Funktionen oder indem sie als gelöscht deklariert werden.



Insbesondere muss die kopierte Klasse Kopiervorgänge explizit deklarieren. Nur eine verschiebbare Klasse muss Verschiebungsoperationen explizit deklarieren. Eine nicht kopierbare / nicht verschiebbare Klasse muss Kopiervorgänge explizit ablehnen ( = löschen ). Das explizite Deklarieren oder Entfernen aller vier Kopier- und Verschiebungsfunktionen ist ebenfalls akzeptabel, jedoch nicht erforderlich. Wenn Sie den Operator "Kopieren und / oder Verschieben" implementieren, müssen Sie auch den entsprechenden Konstruktor erstellen.



class Copyable {
 public:
  Copyable(const Copyable& other) = default;
  Copyable& operator=(const Copyable& other) = default;
  //       (..  )
};
class MoveOnly {
 public:
  MoveOnly(MoveOnly&& other);
  MoveOnly& operator=(MoveOnly&& other);
  //     .  ( )    :
  MoveOnly(const MoveOnly&) = delete;
  MoveOnly& operator=(const MoveOnly&) = delete;
};
class NotCopyableOrMovable {
 public:
  //       
  NotCopyableOrMovable(const NotCopyableOrMovable&) = delete;
  NotCopyableOrMovable& operator=(const NotCopyableOrMovable&)
      = delete;
  //     (),    :
  NotCopyableOrMovable(NotCopyableOrMovable&&) = delete;
  NotCopyableOrMovable& operator=(NotCopyableOrMovable&&)
      = delete;
};

      
      







Beschriebene Funktionsdeklarationen oder -löschungen können in offensichtlichen Fällen weggelassen werden:



  • Wenn die Klasse keinen privaten Abschnitt enthält (z. B. eine Struktur oder eine Schnittstellenklasse), können Kopierbarkeit und Verschiebung durch eine ähnliche Eigenschaft eines öffentlichen Mitglieds deklariert werden.
  • , . , , .
  • , () /, / (.. ). / . .




Ein Typ sollte nicht als kopierbar / verschiebbar deklariert werden, es sei denn, der normale Programmierer versteht die Notwendigkeit dieser Vorgänge oder wenn die Vorgänge sehr ressourcen- und leistungsintensiv sind. Verschiebungsvorgänge für kopierte Typen sind immer eine Leistungsoptimierung, andererseits sind sie eine potenzielle Quelle für Fehler und Komplikationen. Deklarieren Sie daher keine Verschiebungsvorgänge, es sei denn, sie bieten erhebliche Leistungssteigerungen gegenüber dem Kopieren. Im Allgemeinen ist es wünschenswert (wenn Kopiervorgänge für die Klasse deklariert sind), alles so zu gestalten, dass die Standardkopierfunktionen verwendet werden. Überprüfen Sie standardmäßig die Richtigkeit aller Vorgänge.



Aufgrund des Risikos des "Slicing" ist es vorzuziehen, öffentliche Kopier- und Verschiebungsoperatoren für Klassen zu vermeiden, die Sie als Basisklassen verwenden möchten (und vorzugsweise nicht von einer Klasse mit solchen Funktionen erben). Wenn Sie die Basisklasse kopierbar machen müssen, erstellen Sie eine öffentliche virtuelle Funktion Clone () und einen geschützten Kopierkonstruktor, damit die abgeleitete Klasse sie zum Implementieren von Kopiervorgängen verwenden kann.



Strukturen gegen Klassen





Verwenden Sie Strukturen nur für passive Objekte, die Daten speichern. In anderen Fällen verwenden Sie die Klassen ( Klasse ).



Die Schlüsselwörter struct und class sind in C ++ nahezu identisch. Wir haben jedoch unser eigenes Verständnis für jedes Keyword. Verwenden Sie daher dasjenige, das seinem Zweck und seiner Bedeutung entspricht.



Strukturen sollten für passive Objekte nur für die Datenübertragung verwendet werden. Sie können eigene Konstanten haben, aber es sollte keine Funktionalität geben (mit der möglichen Ausnahme von get / set-Funktionen). Alle Felder müssen öffentlich sein und für den direkten Zugriff verfügbar sein. Dies ist der Verwendung von get / set-Funktionen vorzuziehen. Strukturen sollten keine Invarianten (z. B. berechnete Werte) enthalten, die auf Abhängigkeiten zwischen verschiedenen Feldern der Struktur basieren: Die Möglichkeit, die Felder direkt zu ändern, kann die Invariante ungültig machen. Methoden sollten die Verwendung der Struktur nicht einschränken, sondern können Feldern Werte zuweisen: d.h. als Konstruktor, Destruktor oder Funktionen Initialize () , Reset () .



Wenn Sie zusätzliche Funktionen für die Datenverarbeitung oder Invarianten benötigen, sollten Sie vorzugsweise Klassen ( Klassen ) verwenden. Verwenden Sie im Zweifelsfall auch Klassen.



In einigen Fällen ( Vorlagen-Metafunktionen , Merkmale, einige Funktoren) ist es aus Gründen der Konsistenz mit der STL zulässig, Strukturen anstelle von Klassen zu verwenden.



Denken Sie daran, dass Variablen in Strukturen und Klassen in verschiedenen Stilen benannt sind.



Strukturen gegen Paare und Tupel





Wenn einzelne Elemente in einem Datenblock sinnvoll benannt werden können, ist es wünschenswert, Strukturen anstelle von Paaren oder Tupeln zu verwenden.



Während die Verwendung von Paaren und Tupeln verhindert, dass das Rad mit Ihrem eigenen Typ neu erfunden wird, und Sie viel Zeit beim Schreiben von Code sparen , sind Felder mit aussagekräftigen Namen (anstelle von .first , .second oder std :: get <X> ) beim Lesen des Codes leichter zu lesen . Und obwohl C ++ 14 zusätzlich zum Indexzugriff den Typzugriff ( std :: get <Typ> und der Typ muss eindeutig sein) für Tupel hinzufügt , ist der Feldname viel informativer als der Typ.



Paare und Tupel sind im Code geeignet, wenn keine besondere Unterscheidung zwischen den Elementen eines Paares oder Tupels besteht. Sie müssen auch mit vorhandenem Code oder vorhandenen APIs arbeiten.



Erbe





Klassenzusammensetzung ist oft angemessener als Vererbung. Wenn Sie die Vererbung verwenden, machen Sie sie öffentlich .



Definition



Wenn eine untergeordnete Klasse von einer Basisklasse erbt, enthält sie die Definitionen aller Daten und Operationen von der Basisklasse. "Schnittstellenvererbung" ist die Vererbung einer reinen abstrakten Basisklasse (darin sind weder Status noch Methoden definiert). Alles andere ist "Implementierungsvererbung".



Pro



Durch die Vererbung der Implementierung wird die Codegröße reduziert, indem Teile der Basisklasse (die Teil der neuen Klasse wird) wiederverwendet werden. weil Die Vererbung ist eine Deklaration zur Kompilierungszeit. Sie ermöglicht es dem Compiler, die Struktur zu verstehen und Fehler zu finden. Die Schnittstellenvererbung kann verwendet werden, damit die Klasse die erforderliche API unterstützt. Außerdem kann der Compiler Fehler finden, wenn die Klasse die erforderliche Methode der geerbten API nicht definiert.



Nachteile



Bei der Vererbung der Implementierung beginnt der Code zwischen der Basisklasse und der untergeordneten Klasse zu verschwimmen, was das Verständnis des Codes erschweren kann. Außerdem kann die untergeordnete Klasse den Code nicht virtueller Funktionen nicht überschreiben (ihre Implementierung kann nicht geändert werden).



Mehrfachvererbung ist noch problematischer und führt manchmal zu Leistungseinbußen. Oft kann der Leistungsverlust beim Übergang von der Einzelvererbung zur Mehrfachvererbung größer sein als der Übergang von regulären zu virtuellen Funktionen. Es gibt auch einen Schritt von der Mehrfachvererbung zur Rhombik, und dies führt bereits zu Mehrdeutigkeit, Verwirrung und natürlich zu Fehlern.



Urteil



Jede Erbschaft muss öffentlich sein . Wenn Sie es privat machen möchten , ist es besser, ein neues Mitglied mit einer Instanz der Basisklasse hinzuzufügen.



Überbeanspruchen Sie die Vererbung der Implementierung nicht. Klassenzusammensetzung wird oft bevorzugt. Versuchen Sie, die Verwendung der Vererbungssemantik einzuschränken. " Ist »: Balken , Sie können vom Foo erben , wenn ich sagen darf, dass der Balkender Foo ist (dh wo der Foo verwendet wird , können Sie auch den Balken verwenden ).



Protected ( protected, ) führt nur die Funktionen aus, die für die untergeordneten Klassen verfügbar sein sollten. Beachten Sie, dass die Daten privat sein müssen.



Deklarieren Sie explizit die Überschreibungen virtueller Funktionen / Destruktoren mithilfe von Bezeichnern: entweder überschreiben oder (falls erforderlich) endgültig . Verwenden Sie den virtuellen Bezeichner nicht, wenn Sie Funktionen überschreiben. Erläuterung: Eine Funktion oder ein Destruktor, die als überschrieben oder endgültig markiert, aber nicht virtuell ist, wird einfach nicht kompiliert (was dazu beiträgt, häufige Fehler zu erkennen). Auch Spezifizierer funktionieren wie Dokumentation. Wenn keine Spezifizierer vorhanden sind, muss der Programmierer die gesamte Hierarchie überprüfen, um die Virtualität der Funktion zu verdeutlichen.



Mehrfachvererbung ist zulässig, jedoch Mehrfachvererbung Implementierung wird vom Wort überhaupt nicht empfohlen.



Überlastung des Bedieners





Überladen Sie die Bediener so vernünftig wie möglich. Verwenden Sie keine benutzerdefinierten Literale.



Bestimmung von



C ++ Code ermöglicht der Benutzer des integrierten Operatoren außer Kraft zu setzen mit dem Schlüsselwort Operator und den Benutzertyp als eines des Parameters; Mit dem Operator können Sie auch neue Literale mit dem Operator "" definieren . Sie können auch Casting-Funktionen wie operator bool () erstellen .



Pro



Durch die Verwendung der Operatorüberladung für benutzerdefinierte Typen (ähnlich wie bei integrierten Typen) kann Ihr Code präziser und intuitiver gestaltet werden. Überladene Operatoren entsprechen bestimmten Operationen (z. B. == , < , = und << ). Wenn der Code der Logik zum Anwenden dieser Operationen folgt, können benutzerdefinierte Typen klarer gestaltet und verwendet werden, wenn mit externen Bibliotheken gearbeitet wird, die auf diesen Operationen basieren.



Benutzerdefinierte Literale sind eine sehr effiziente Methode zum Erstellen benutzerdefinierter Objekte.



Vs



  • (, ) — , , .
  • , .
  • , , .
  • , , .
  • , .
  • / ( ), «» . , foo < bar &foo < &bar; .
  • . & , . &&, || , () ( ) .
  • , . , .
  • (UDL) , C++ . : «Hello World»sv std::string_view(«Hello World»). , .
  • weil Für die UDL ist kein Namespace angegeben. Sie müssen entweder eine using-Direktive (die verboten ist ) oder eine using-Deklaration (die ebenfalls verboten ist (in Header-Dateien) verwenden , es sei denn, die importierten Namen sind Teil der in der Header-Datei angezeigten Schnittstelle). Für solche Header-Dateien ist es am besten, UDL-Suffixe zu vermeiden, und es ist wünschenswert, Abhängigkeiten zwischen Literalen zu vermeiden, die sich in der Header- und Quelldatei unterscheiden.




Urteil



Definieren Sie überladene Operatoren nur, wenn ihre Bedeutung klar, verständlich und mit der allgemeinen Logik vereinbar ist. Verwenden Sie beispielsweise | im Sinne der OP-Operation; Die Implementierung einer Pipe-Logik ist keine gute Idee.



Definieren Sie Operatoren nur für Ihre eigenen Typen, und zwar im selben Header und in derselben Quelldatei sowie im selben Namespace. Infolgedessen stehen Operatoren an derselben Stelle wie die Typen selbst zur Verfügung, und das Risiko mehrerer Definitionen ist minimal. Vermeiden Sie nach Möglichkeit, Operatoren als Vorlagen zu definieren. Sie müssen mit allen Vorlagenargumenten übereinstimmen. Wenn Sie einen Operator definieren, definieren Sie auch "Geschwister". Und achten Sie auf die Konsistenz der Ergebnisse, die sie zurückgeben. Wenn Sie beispielsweise den Operator < definieren , definieren Sie alle Vergleichsoperatoren und stellen Sie sicher, dass die Operatoren < und > niemals true für dieselben Argumente zurückgeben.



Es ist wünschenswert, unveränderliche binäre Operatoren als externe Funktionen (Nichtmitglieder) zu definieren. Wenn der binäre Operator als Mitglied der Klasse deklariert ist, kann die implizite Konvertierung auf das rechte Argument angewendet werden, nicht jedoch auf das linke. Dies kann für Programmierer etwas frustrierend sein, wenn (zum Beispiel) der Code a <b kompiliert wird, b <a jedoch nicht.



Sie müssen nicht versuchen, Operator-Overrides zu umgehen. Wenn ein Vergleich (oder eine Zuweisungs- und Ausgabefunktion) erforderlich ist, ist es besser, == (oder = und << ) anstelle von Equals () , CopyFrom () und zu definieren PrintTo () . Umgekehrt müssen Sie einen Operator nicht neu definieren, nur weil externe Bibliotheken dies erwarten. Wenn der Datentyp beispielsweise nicht bestellt werden kann und Sie ihn in std :: set speichern möchten , ist es besser, eine benutzerdefinierte Vergleichsfunktion zu erstellen und den Operator < nicht zu verwenden . && , ||



nicht überschreiben , , (Komma) oder einstellige & . Überschreiben Sie den Operator "" nicht , d. H. Fügen Sie keine eigenen Literale hinzu. Verwenden Sie keine zuvor definierten Literale (einschließlich der Standardbibliothek und darüber hinaus).



Zusätzliche Informationen:

Die Typkonvertierung wird im Abschnitt über implizite Konvertierungen beschrieben . Der Operator = wird in den Kopierkonstruktor geschrieben . Das Thema Überladen << für die Arbeit mit Streams wird in Streams behandelt . Sie können sich auch mit den Regeln aus dem Abschnitt über Überladungsfunktionen vertraut machen , die auch für Bediener geeignet sind.



Zugriff auf Klassenmitglieder





Machen Sie Klassendaten immer privat , mit Ausnahme von Konstanten . Dies vereinfacht die Verwendung von Invarianten durch Hinzufügen einfachster (häufig konstanter) Zugriffsfunktionen.



Es ist zulässig, Klassendaten als geschützt für die Verwendung in Testklassen (z. B. bei Verwendung von Google Test ) oder ähnlichen Fällen zu deklarieren .



Ankündigungsverfahren





Platzieren Sie ähnliche Anzeigen an einem Ort und bringen Sie gemeinsame Teile zur Sprache.



Die Klassendefinition beginnt normalerweise mit einem Abschnitt der öffentlichen : , geht weiter geschützt: und dann der privaten : . Geben Sie keine leeren Abschnitte an.



Gruppieren Sie in jedem Abschnitt ähnliche Erklärungen. Die bevorzugte Reihenfolge sind Typen (einschließlich typedef , using , verschachtelte Klassen und Strukturen), Konstanten, Factory-Methoden, Konstruktoren, Zuweisungsoperatoren, Destruktoren, andere Methoden und Datenelemente.



Platzieren Sie keine umfangreichen Methodendefinitionen in der Klassendefinition. Normalerweise werden nur triviale, sehr kurze oder leistungskritische Methoden in die Klassendefinition "eingebunden". Siehe auch Inline-Funktionen .



All Articles