Kann mich bitte jemand auf einige nette Ressourcen hinweisen, um verschachtelte Klassen zu verstehen und zu verwenden? Ich habe Material wie Programmierprinzipien und solche Dinge wie dieses IBM Knowledge Center - Verschachtelte Klassen
Aber ich habe immer noch Probleme, ihren Zweck zu verstehen. Könnte mir bitte jemand helfen?
c++
nested
inner-classes
mit Brille
quelle
quelle
typedef
. 3. weil sie in einer Umgebung, in der es bereits schwierig ist, lange Zeilen zu vermeiden, eine zusätzliche Einrückungsstufe hinzufügen 4. weil Sie zwei konzeptionell getrennte Objekte in einer einzigenclass
Deklaration deklarieren usw.Antworten:
Verschachtelte Klassen sind cool, um Implementierungsdetails auszublenden.
Aufführen:
Hier möchte ich Node nicht verfügbar machen, da andere Benutzer sich möglicherweise für die Verwendung der Klasse entscheiden. Dies würde mich daran hindern, meine Klasse zu aktualisieren, da alles, was verfügbar gemacht wird, Teil der öffentlichen API ist und für immer beibehalten werden muss . Indem ich die Klasse privat mache, verstecke ich nicht nur die Implementierung, sondern sage auch, dass dies meine ist, und ich kann sie jederzeit ändern, sodass Sie sie nicht verwenden können.
Schauen Sie sich an
std::list
oderstd::map
sie enthalten alle versteckte Klassen (oder doch?). Der Punkt ist, dass sie möglicherweise oder möglicherweise nicht, aber da die Implementierung privat und versteckt ist, konnten die Builder der STL den Code aktualisieren, ohne die Verwendung des Codes zu beeinflussen, oder viel altes Gepäck in der STL herumliegen lassen, weil sie es benötigen um die Abwärtskompatibilität mit einem Dummkopf aufrechtzuerhalten, der beschlossen hat, die darin verborgene Node-Klasse zu verwendenlist
.quelle
Node
sollte es überhaupt nicht in der Header-Datei verfügbar gemacht werden.detail
Konvention: Abhängig von solchen Konventionen muss man sich selbst im Auge behalten. Es ist besser, sich auf den Compiler zu verlassen, der sie für Sie verfolgt.Verschachtelte Klassen sind wie normale Klassen, aber:
Einige Beispiele:
Klasse öffentlich verschachteln, um sie in einen Bereich der relevanten Klasse zu setzen
Angenommen, Sie möchten eine Klasse haben, die Klassenobjekte
SomeSpecificCollection
aggregiertElement
. Sie können dann entweder:Deklarieren Sie zwei Klassen:
SomeSpecificCollection
undElement
- schlecht, da der Name "Element" allgemein genug ist, um einen möglichen Namenskonflikt zu verursachenFühren Sie einen Namespace ein
someSpecificCollection
und deklarieren Sie KlassensomeSpecificCollection::Collection
undsomeSpecificCollection::Element
. Kein Risiko eines Namenskonflikts, aber kann es noch ausführlicher werden?deklarieren Sie zwei globale Klassen
SomeSpecificCollection
undSomeSpecificCollectionElement
- was kleinere Nachteile hat, aber wahrscheinlich in Ordnung ist.Deklarieren Sie globale Klasse
SomeSpecificCollection
und KlasseElement
als verschachtelte Klasse. Dann:SomeSpecificCollection
beziehen Sie sich auf gerechtElement
und überall sonst alsSomeSpecificCollection::Element
- was + aussieht - das gleiche wie 3., aber klarerSomeSpecificCollection
auch eine Klasse ist.Meiner Meinung nach ist die letzte Variante definitiv das intuitivste und damit beste Design.
Lassen Sie mich betonen - Es ist kein großer Unterschied, zwei globale Klassen mit ausführlicheren Namen zu erstellen. Es ist nur ein kleines Detail, aber imho macht es den Code klarer.
Einführung eines anderen Bereichs innerhalb eines Klassenbereichs
Dies ist besonders nützlich für die Einführung von Typedefs oder Enums. Ich werde hier nur ein Codebeispiel posten:
Man wird dann anrufen:
Wenn man sich jedoch Vorschläge zur Code-Vervollständigung ansieht
Product::
, werden oft alle möglichen Aufzählungswerte (BOX, FANCY, CRATE) aufgelistet, und es ist leicht, hier einen Fehler zu machen (die stark typisierten Aufzählungen von C ++ 0x lösen das irgendwie, aber egal ).Wenn Sie jedoch mithilfe verschachtelter Klassen zusätzlichen Bereich für diese Aufzählungen einführen, könnte dies folgendermaßen aussehen:
Dann sieht der Anruf so aus:
Product::ProductType::
Wenn Sie dann eine IDE eingeben , erhalten Sie nur die Aufzählungen aus dem gewünschten vorgeschlagenen Bereich. Dies verringert auch das Fehlerrisiko.Natürlich wird dies für kleine Klassen möglicherweise nicht benötigt, aber wenn man viele Aufzählungen hat, erleichtert dies den Client-Programmierern die Arbeit.
Auf die gleiche Weise könnten Sie eine große Anzahl von Typedefs in einer Vorlage "organisieren", falls Sie dies jemals benötigen würden. Es ist manchmal ein nützliches Muster.
Die PIMPL-Sprache
Die PIMPL (kurz für Pointer to IMPLementation) ist eine nützliche Redewendung, um die Implementierungsdetails einer Klasse aus dem Header zu entfernen. Dies reduziert die Notwendigkeit, Klassen abhängig vom Header der Klasse neu zu kompilieren, wenn sich der "Implementierung" -Teil des Headers ändert.
Es wird normalerweise mit einer verschachtelten Klasse implementiert:
Xh:
X.cpp:
Dies ist besonders nützlich, wenn für die vollständige Klassendefinition die Definition von Typen aus einer externen Bibliothek erforderlich ist, die eine schwere oder nur hässliche Header-Datei enthält (nehmen Sie WinAPI). Wenn Sie PIMPL verwenden, können Sie WinAPI-spezifische Funktionen nur in einschließen
.cpp
und niemals einschließen.h
.quelle
struct Impl; std::auto_ptr<Impl> impl;
Dieser Fehler wurde von Herb Sutter populär gemacht. Verwenden Sie auto_ptr nicht für unvollständige Typen oder treffen Sie zumindest Vorsichtsmaßnahmen, um zu vermeiden, dass falscher Code generiert wird.auto_ptr
in den meisten Implementierungen einen unvollständigen Typ deklarieren , aber technisch gesehen ist es UB im Gegensatz zu einigen Vorlagen in C ++ 0x (z. B.unique_ptr
), bei denen explizit angegeben wurde, dass der Vorlagenparameter möglicherweise ist ein unvollständiger Typ und wo genau der Typ vollständig sein muss. (zB Verwendung von~unique_ptr
)T
vonunique_ptr
kann ein unvollständiger Typ sein."enum class
.Ich benutze nicht viel verschachtelte Klassen, aber ich benutze sie ab und zu. Insbesondere, wenn ich einen Datentyp definiere und dann einen STL-Funktor definieren möchte, der für diesen Datentyp entwickelt wurde.
Betrachten Sie beispielsweise eine generische
Field
Klasse mit einer ID-Nummer, einem Typcode und einem Feldnamen. Wenn ich einesvector
dieserField
s nach ID-Nummer oder Name durchsuchen möchte , kann ich einen Funktor dafür erstellen:Dann kann Code, der nach diesen
Field
s suchen muss, denmatch
Gültigkeitsbereich innerhalb derField
Klasse selbst verwenden:quelle
Man kann ein Builder-Muster mit verschachtelter Klasse implementieren . Besonders in C ++ finde ich es semantisch sauberer. Beispielsweise:
Eher, als:
quelle