Ich unterrichte mich nur selbst in C ++ - Namespaces (mit C # -Hintergrund) und beginne wirklich zu denken, dass verschachtelte Namespaces trotz all der Dinge, die C ++ besser macht als die meisten anderen Sprachen, nicht dazu gehören!
Habe ich Recht, wenn ich denke, dass ich Folgendes tun muss, um einige verschachtelte Namespaces zu deklarieren:
namespace tier1
{
namespace tier2
{
namespace tier3
{
/* then start your normal code nesting */
}
}
}
Im Gegensatz zu:
namespace tier1::tier2::tier3
{
}
à la C #?
Dies wird noch wahnsinniger, wenn ich weiterleiten muss:
namespace tier1
{
namespace tier2
{
namespace forward_declared_namespace
{
myType myVar; // forward declare
}
namespace tier3
{
/* then start your normal code nesting */
class myClass
{
forward_declared_namespace::myType myMember;
}
}
}
}
In Anbetracht dessen, dass ein typisches System, das ich entwickle, besteht aus:
MyCompany::MySolution::MyProject::System::[PossibleSections]::Type
Ist dies der Grund, warum Namespaces in C ++ - Beispielen nicht häufig verwendet werden? Oder normalerweise nur einzelne (nicht verschachtelte) Namespaces?
AKTUALISIEREN
Für alle Interessierten ist dies der Grund , warum ich dieses Problem angegangen bin .
quelle
namespace tier1::tier2::tier3
.Antworten:
C ++ - Namespaces waren nicht als Entwurfsmechanismus gedacht - sie dienen lediglich dazu, Namenskonflikte zu verhindern. In 99,99% der Situationen möchten oder müssen Sie wirklich keine verschachtelten Namespaces verwenden.
Ein gutes Beispiel für die korrekte Verwendung von Namespaces in C ++ ist die C ++ - Standardbibliothek. Alles in dieser ziemlich großen Bibliothek befindet sich in einem einzigen Namespace namens std Es besteht kein Versuch oder Bedarf, die Bibliothek in (zum Beispiel) einen E / A-Sub-Namespace, einen mathematischen Sub-Namespace oder einen Container-Sub-Namespace aufzuteilen etc.
Das grundlegende Werkzeug für die Modellierung in C ++ ist die Klasse (und in gewissem Maße die Vorlage), nicht der Namespace. Wenn Sie eine Verschachtelung benötigen, sollten Sie verschachtelte Klassen verwenden, die gegenüber Namespaces die folgenden Vorteile bieten:
Wenn Sie diese berücksichtigt haben und dennoch verschachtelte Namespaces verwenden möchten, ist dies technisch gesehen nichts Falsches daran, sie auf diese Weise zu verwenden.
quelle
C ++ - Namespaces waren eine enorme Verbesserung gegenüber dem vorherigen Angebot (dh überhaupt keine Namespaces). C # -Namensräume haben das Konzept erweitert und sind damit gelaufen. Ich würde Ihnen raten, Ihre Namespaces in einer einfachen flachen Struktur zu halten.
EDIT Do Sie darauf hinweisen , dass aufgrund der Unzulänglichkeiten ich hier skizziert habe?
Einfach "Ja". C ++ - Namespaces wurden nicht entwickelt, um Ihnen zu helfen, Ihre Logik und Bibliotheken so zu partitionieren, wie sie es in C # tun.
Der Zweck von C ++ - Namespaces besteht darin, das Problem der realen Welt zu stoppen, auf das C-Entwickler stoßen, bei dem Namenskollisionen auftreten, wenn zwei Bibliotheken von Drittanbietern verwendet werden, die dieselben Funktionsnamen exportieren. C-Entwickler hatten verschiedene Problemumgehungen, aber es könnte ein ernsthafter Schmerz sein.
Die Idee war, dass die STL usw. den
std::
Namespace hat, die von "XYZ Corp" bereitgestellten Bibliotheken einenxyz::
Namespace haben würden, wenn Sie für "ABC corp" arbeiten würden, würden Sie alle Ihre Sachen in einem einzigenabc::
Namespace zusammenfassen.quelle
abc::PROJECT
Sub-Namespaces zu haben. Und wenn größere Projekte klare Abschnitte haben, ist dasabc::PROJECT::SECTION
auch sinnvoll. Sogar die C ++ - Götter haben verstanden, dass es Abschnitte gibt, und Sub-Namespaces wiestd::chrono
oder erstelltstd::ios
.Was ich mache, wenn ich vorwärts deklariere, sieht so aus:
namespace abc { namespace sub { namespace subsub { class MyClass; }}}
Meine Vorwärtserklärungen sind in einer Zeile zusammengefasst. Die Lesbarkeit der Weiterleitungsdeklaration wird im Gegenzug für die Lesbarkeit des restlichen Codes geopfert. Und für Definitionen verwende ich auch keine Einrückungen:
namespace abc { namespace sub { namespace subsub { class MyClass { public: MyClass(); void normalIntendationsHere() const; }; } } }
Die Verwendung dieses Stils erfordert am Anfang ein wenig Disziplin, ist aber der beste Kompromiss für mich.
Seit C ++ 17 können Sie den Namespace mit einer vom Autor der Frage vorgeschlagenen Syntax deklarieren.
namespace A::B::C { ... }
https://en.cppreference.com/w/cpp/language/namespace
quelle
Zumindest als kleine Hilfe können Sie in einigen Fällen Folgendes tun:
namespace foo = A::B::C::D;
Und dann A :: B :: C :: D als foo bezeichnen. Aber nur in einigen Fällen.
quelle
Sie können Einrückungen überspringen. Ich schreibe oft
namespace myLib { namespace details { /* source code */ } } /* myLib::details */
C ++ - Quellcode wird schließlich in Binärform kompiliert, im Gegensatz zu C # / Java, das in der Binärdatei verbleibt. Daher bietet der Namespace nur eine gute Lösung für den Konflikt bei der Benennung von Variablen. Es ist nicht für die Klassenhierarchie vorgesehen.
Ich behalte oft ein oder zwei Namespace-Ebenen im Code.
quelle
Zunächst können Sie das Einrücken von Namespaces vermeiden, da es dafür keinen Grund gibt.
Die Verwendung von Namespaces in den Beispielen zeigt nicht die Leistung von Namespaces an. Und ihre Macht für mich besteht darin, Domain-Bereiche voneinander zu trennen. Teilen Sie Utility-Klassen von den Business-Klassen.
Mischen Sie nur keine unterschiedlichen Namespace-Hierarchien in der einen .h-Datei. Namespaces sind eine Art zusätzlicher Kommentar für Ihre Schnittstelle der Funktionsdeklaration. Das Betrachten von Namespaces und Klassennamen sollte eine Menge Dinge erklären.
namespace product { namespace DAO { class Entity { };
quelle
Sie überbeanspruchen sie (und Sie erhalten nichts zurück).
quelle
langs::cpp
ich immer noch alles sehen kannlangs
, ohne wirklich isoliert zu seincpp
... Dies ist genau das Problem mit C ++ - Namespaces und warum flache Struktur wird vorgeschlagen. in Rust Module, zum Beispiel, könnten Sie nicht Zuganglangs::*
voncpp
, und ich habe nie gesagt , dass dies schlecht ist, ist es großartig, aber in C ++ ist es nicht so.Ich habe festgestellt, dass Sie den c # -Namensraum so nachahmen können.
namespace ABC_Maths{ class POINT2{}; class Complex{}; } namespace ABC_Maths_Conversion{ ABC_MATHS::Complex ComplexFromPOINT2(ABC_MATHS::POINT2) {return new ABC_MATHS::Complex();} ABC_MATHS::POINT4 POINT2FromComplex(ABC_MATHS::COMPLEX) {return new ABC_MATHS::POINT2();} } namespace ABC { }
Aber Code scheint nicht sehr ordentlich zu sein. und ich würde erwarten, langwierigen Gebrauch zu haben
Es ist besser, so viel Funktionalität wie möglich in Klassen zu verschachteln
namespace ABC{ class Maths{ public: class POINT2{}; class Complex:POINT2{}; class Conversion{ public: static Maths.Complex ComplexFromPOINT2(MATHS.POINT2 p) {return new MATHS.Complex();} static MATHS.POINT2 POINT2FromComplex(MATHS.COMPLEX p) {return new ABC::MATHS.POINT2();}// Can reference via the namespace if needed } /*end ABC namespace*/
Und das ist noch ein bisschen langwierig. fühlt sich aber ein bisschen OO an.
Und hört, wie es am besten gemacht scheint
namespace ABC { class POINT2{}; class Complex:POINT2{}; Complex ComplexFromPOINT2(POINT2 p){return new Complex();} POINT2 POINT2FromComplex(Complex){return new POINT2();} }
hört, wie Verwendungen aussehen würden
int main() { ABC_Maths::Complex p = ABC_Maths_Conversion::ComplexFromPOINT2(new ABC_MATHS::POINT2()); // or THE CLASS WAY ABC.Maths.Complex p = ABC.Maths.Conversion.ComplexFromPOINT2(new ABC.Maths.POINT2()); // or if in/using the ABC namespace Maths.Complex p = Maths.Conversion.ComplexFromPOINT2(new Maths.POINT2()); // and in the final case ABC::Complex p = ABC::ComplexFromPOINT2(new ABC::POINT2()); }
Es war interessant herauszufinden, warum ich nie C ++ - Namespaces verwendet habe, wie ich es mit c # tun würde. Es wäre zu langwierig und würde niemals so funktionieren wie c # -Namensräume.
Die beste Verwendung für Namespaces in C ++ besteht darin, zu verhindern, dass meine Super-Cout-Funktion (die jedes Mal klingelt, wenn sie aufgerufen wird) mit der Funktion std :: cout verwechselt wird (was weitaus weniger beeindruckend ist).
Nur weil c # und c ++ Namespaces haben, bedeutet dies nicht, dass Namespace dasselbe bedeutet. Sie sind unterschiedlich, aber ähnlich. Die Idee für c # -Namensräume muss aus c ++ - Namespaces stammen. Jemand muss gesehen haben, was eine ähnliche, aber andere Sache tun könnte, und hatte nicht mehr genug Vorstellungskraft, um ihm seinen eigenen ursprünglichen Namen wie "ClassPath" zu geben, was eher sinnvoll wäre, als ein Weg zum Unterricht zu sein, als ihn bereitzustellen Benennen von Räumen, in denen jeder Raum den gleichen Namen haben kann
Ich hoffe das hilft jemandem
Ich habe vergessen zu sagen, dass all diese Möglichkeiten gültig sind und eine moderate Verwendung der ersten beiden in die dritte integriert werden kann, um eine Bibliothek zu erstellen, die Sinn macht, dh (kein gutes Beispiel)
und
So können Sie die Genauigkeitsstufe mithilfe von ändern
#define PREC _DOUBLE // or #define PREC _INT
und dann Erstellen einer Instanz von PREC :: POINT2 für POINT2 mit doppelter Genauigkeit
Mit C # - oder Java-Namespaces ist das nicht einfach
das ist natürlich nur ein beispiel. Überlegen Sie, wie die Verwendung gelesen wird.
quelle
Manchmal deklariere ich tiefe Namespaces als ein paar Makros in einem separaten Header
Namespace.h
#define NAMESPACE_TIER1_TIER2_TIER3 \ namespace tier1 { \ namespace tier2 { \ namespace tier3 { #define END_NAMESPACE_TIER1_TIER2_TIER3 }}}
Um es woanders zu verwenden:
anotherfile.h
#include "./namespace.h" NAMESPACE_TIER1_TIER2_TIER3; /* Code here */ END_NAMESPACE_TIER1_TIER2_TIER3;
Die redundanten Semikolons nach dem Makro sollen zusätzliche Einrückungen vermeiden.
quelle
NAMESPACE_TIER1_TIER2_TIER3
undEND_NAMESPACE_TEIR1_TIER2_TIER3
warum nicht einfach schreibennamespace tier1{namespace tier2{namespace tier3{
und}}}
? Insgesamt ist es tatsächlich kürzer.