Die Frage, wo ich Funktionen platzieren soll, die sich nicht auf eine Klasse beziehen, hat einige Debatten ausgelöst, ob es in C ++ sinnvoll ist, Dienstprogrammfunktionen in einer Klasse zu kombinieren, oder ob sie nur als freie Funktionen in einem Namespace vorhanden sind.
Ich komme aus einem C # -Hintergrund, in dem die letztere Option nicht existiert, und tendiere daher natürlich dazu, statische Klassen in dem kleinen C ++ - Code zu verwenden, den ich schreibe. Die am höchsten bewertete Antwort auf diese Frage sowie mehrere Kommentare besagen jedoch, dass freie Funktionen vorzuziehen sind, selbst wenn man annimmt, dass statische Klassen ein Anti-Muster sind. Warum ist das so in C ++? Zumindest an der Oberfläche scheinen statische Methoden für eine Klasse von freien Funktionen in einem Namespace nicht zu unterscheiden zu sein. Warum also die Präferenz für Letzteres?
Wäre es anders, wenn die Sammlung von Utility-Funktionen einige gemeinsam genutzte Daten erfordern würde, z. B. einen Cache, den man in einem privaten statischen Feld speichern könnte?
quelle
Antworten:
Ich denke zu antworten, dass wir die Absichten von Klassen und Namespaces vergleichen sollten. Laut Wikipedia:
Klasse
Namespace
Was möchten Sie nun erreichen, indem Sie die Funktionen in eine Klasse (statisch) oder einen Namespace einfügen? Ich würde wetten, dass die Definition eines Namensraums Ihre Absicht besser beschreibt - alles, was Sie wollen, ist ein Container für Ihre Funktionen. Sie benötigen keine der in der Klassendefinition beschriebenen Funktionen. Beachten Sie, dass die ersten Wörter der Klassendefinition " In der objektorientierten Programmierung " lauten und dennoch nichts an einer Sammlung von Funktionen objektorientiert ist.
Es gibt wahrscheinlich auch technische Gründe, aber da jemand aus Java kommt und versucht, mich mit der Multiparadigmasprache C ++ vertraut zu machen, ist die naheliegendste Antwort für mich: Weil wir kein OO benötigen, um dies zu erreichen.
quelle
Ich würde das sehr vorsichtig als Anti-Pattern bezeichnen. Namespaces werden normalerweise bevorzugt, aber da es keine Namespace-Vorlagen gibt und Namespaces nicht als Vorlagenparameter übergeben werden können, ist die Verwendung von Klassen mit ausschließlich statischen Elementen durchaus üblich.
quelle
Früher musste ich einen FORTRAN-Kurs belegen. Zu diesem Zeitpunkt kannte ich andere Gebotssprachen und dachte mir, ich könnte FORTRAN ohne viel Lernen machen. Als ich meine ersten Hausaufgaben abgab, schickte der Professor sie mir zurück und bat, sie zu wiederholen: Er sagte, dass Pascal-Programme, die in FORTRAN-Syntax geschrieben sind, nicht als gültige Einsendungen gelten.
Hier spielt ein ähnliches Problem eine Rolle: Die Verwendung statischer Klassen zum Hosten von Dienstprogrammfunktionen in C ++ ist eine Fremdsprache für C ++.
Wie Sie in Ihrer Frage erwähnt haben, ist die Verwendung statischer Klassen für Dienstprogrammfunktionen in C # eine Notwendigkeit: Freistehende Funktionen sind in C # einfach keine Option. Die Sprache, die benötigt wird, um ein Muster zu entwickeln, mit dem Programmierer freistehende Funktionen auf andere Weise definieren können - nämlich innerhalb statischer Dienstprogrammklassen. Dies war ein Wort-für-Wort-Java-Trick: Zum Beispiel sind java.lang.Math und System.Math von .NET nahezu isomorph.
C ++ bietet jedoch Namespaces, eine andere Möglichkeit, um dasselbe Ziel zu erreichen, und verwendet sie aktiv bei der Implementierung seiner Standardbibliothek. Das Hinzufügen einer zusätzlichen Schicht statischer Klassen ist nicht nur unnötig, sondern auch für Leser ohne C # - oder Java-Hintergrund etwas weniger intuitiv. In gewisser Weise führen Sie eine "Kreditübersetzung" in die Sprache ein, die von Haus aus ausgedrückt werden kann.
Wenn Ihre Funktionen Daten gemeinsam nutzen müssen, ist die Situation anders. Da Ihre Funktionen nicht mehr voneinander unabhängig sind , wird das Singleton-Muster zur bevorzugten Methode, um diese Anforderung zu erfüllen.
quelle
Vielleicht müssen Sie sich fragen, warum Sie eine rein statische Klasse wollen?
Der einzige Grund, den ich mir vorstellen kann, ist, dass andere Sprachen (Java und C #), bei denen alles eine Klasse ist, dies erfordern. Diese Sprachen können überhaupt keine Funktionen der obersten Ebene erstellen, daher haben sie einen Trick erfunden, um sie beizubehalten, und das war die statische Elementfunktion. Sie sind eine Art Workaround, aber C ++ benötigt so etwas nicht. Sie können direkt brandneue, unabhängige Funktionen auf höchster Ebene erstellen.
Wenn Sie Funktionen benötigen, die für ein bestimmtes Datenelement ausgeführt werden, ist es sinnvoll, sie in einer Klasse zu bündeln, die die Daten enthält. In diesem Fall sind diese Funktionen jedoch keine weiteren Mitglieder dieser Klasse, die für die Daten der Klasse ausgeführt werden.
Wenn Sie eine Funktion haben, die nicht mit einem bestimmten Datentyp arbeitet (ich verwende das Wort hier, da Klassen Wege sind, um neue Datentypen zu definieren), dann ist es wirklich ein Anti-Pattern, sie in eine Klasse zu schieben, für keinen anderen als semantische Zwecke.
quelle
Mit anderen Worten, eine Klasse anstelle eines Namespaces zu haben, hat keine Vorteile.
Zum einen erspart es Ihnen, die
static
ganze Zeit zu tippen, obwohl das wohl ein eher geringfügiger Vorteil ist.Der Hauptvorteil ist, dass es das am wenigsten leistungsfähige Werkzeug für den Job ist. Klassen können zum Erstellen von Objekten verwendet werden. Sie können als Variablentyp oder als Vorlagenargumente verwendet werden. Keine dieser Funktionen möchten Sie für Ihre Funktionssammlung verwenden. Verwenden Sie daher vorzugsweise ein Tool ohne diese Funktionen, damit die Klasse nicht versehentlich missbraucht wird.
Wenn Sie einem Namespace folgen, wird den Benutzern Ihres Codes auch sofort klar, dass es sich um eine Sammlung von Funktionen und nicht um eine Blaupause handelt, aus der Objekte erstellt werden können.
quelle
Eine rein statische Klasse wird die Arbeit erledigen, aber es ist, als würde man mit einem 53-Fuß-Sattelschlepper zum Supermarkt fahren, um Chips und Salsa zu kaufen, wenn eine viertürige Limousine dies tut (dh es ist übertrieben). Klassen sind mit einem geringen zusätzlichen Aufwand verbunden, und ihre Existenz könnte den Eindruck erwecken, dass es eine gute Idee sein könnte, eine Instanz zu erstellen. Die freien Funktionen von C ++ (und C, wo alle Funktionen frei sind) machen das nicht; Sie sind nur Funktionen und sonst nichts.
Nicht wirklich. Der ursprüngliche Zweck von
static
C (und höherem C ++) bestand darin, dauerhaften Speicher anzubieten, der auf jeder Ebene des Umfangs verwendet werden kann:Sie können den Bereich auf die Ebene einer Datei verschieben, wodurch er für alle engeren Bereiche sichtbar ist, jedoch nicht außerhalb der Datei:
Ein privates statisches Klassenmitglied in C ++ dient demselben Zweck, ist jedoch auf den Bereich einer Klasse beschränkt. Die im
counter()
obigen Beispiel verwendete Technik funktioniert auch innerhalb von C ++ - Methoden. Ich würde diese Vorgehensweise empfehlen, wenn die Variable nicht für die gesamte Klasse sichtbar sein muss.quelle
Wenn eine Funktion keinen Zustand beibehält und wieder eintritt, scheint es nicht sinnvoll zu sein, sie innerhalb einer Klasse zu verschieben (es sei denn, dies wird von der Sprache erzwungen). Wenn die Funktion einen bestimmten Status beibehält (z. B. nur mit einem statischen Mutex threadsicher gemacht werden kann), erscheinen statische Methoden für eine Klasse angemessen.
quelle
Ein Großteil des Diskurses zu diesem Thema ist sinnvoll, obwohl C ++ etwas sehr Grundlegendes hat, das
namespaces
undclass
es /struct
s sehr unterschiedlich macht.Statische Klassen (Klassen, in denen alle Mitglieder statisch sind und die Klasse niemals instanziiert wird) sind selbst Objekte. Sie sind nicht einfach
namespace
Funktionen zu enthalten.Die Template-Metaprogrammierung ermöglicht die Verwendung einer statischen Klasse als Objekt zur Kompilierungszeit.
Bedenken Sie:
Um es zu benutzen, brauchen wir Funktionen, die in einer Klasse enthalten sind. Ein Namespace funktioniert hier nicht. Erwägen:
Um es jetzt zu benutzen:
Solange ein neuer Allokator eine
allocate
undrelease
kompatible Funktion verfügbar macht , ist der Wechsel zu einem neuen Allokator einfach.Dies kann mit Namespaces nicht erreicht werden.
Benötigen Sie immer Funktionen, um Teil einer Klasse zu sein? Nein.
Ist die Verwendung einer statischen Klasse ein Anti-Pattern? Es kommt auf den Kontext an.
In diesem Fall wird das, was Sie erreichen möchten, wahrscheinlich am besten durch objektorientierte Programmierung erreicht.
quelle
Wie John Carmack berühmt sagte:
"Manchmal ist die elegante Implementierung nur eine Funktion. Keine Methode. Keine Klasse. Kein Framework. Nur eine Funktion."
Ich denke, das fasst es ziemlich gut zusammen. Warum würdest du es mit Nachdruck zu einer Klasse machen, wenn es eindeutig keine Klasse ist? C ++ hat den Luxus, dass Sie tatsächlich Funktionen verwenden können; In Java ist alles eine Methode. Dann brauchen Sie Utility-Klassen und viele von ihnen.
quelle