371 Stimmen

Namensraum + Funktionen versus statische Methoden in einer Klasse

Nehmen wir an, ich habe eine Reihe von zusammenhängenden Funktionen oder werde eine solche schreiben. Nehmen wir an, sie sind mathematisch verwandt. Organisatorisch sollte ich:

  1. Schreiben Sie diese Funktionen und fügen Sie sie in meine MyMath Namespace und verweisen auf sie über MyMath::XYZ()
  2. Erstellen Sie eine Klasse namens MyMath und machen diese Methoden statisch und verweisen auf die ähnlich MyMath::XYZ()

Warum sollte ich das eine dem anderen vorziehen, wenn es um die Organisation meiner Software geht?

295voto

paercebal Punkte 78198

Standardmäßig werden Funktionen mit Namensraum verwendet.

Klassen sind dazu da, Objekte zu erstellen, nicht um Namensräume zu ersetzen.

In objektorientiertem Code

Scott Meyers hat für sein Buch Effective C++ einen ganzen Artikel zu diesem Thema geschrieben, "Prefer non-member non-friend functions to member functions". Ich fand einen Online-Verweis auf dieses Prinzip in einem Artikel von Herb Sutter: http://www.gotw.ca/gotw/084.htm

Das ist wichtig zu wissen: In C++ gehören Funktionen, die sich im gleichen Namensraum wie eine Klasse befinden und die diese Klasse als Parameter haben, zur Schnittstelle dieser Klasse (weil ADL sucht diese Funktionen bei der Auflösung von Funktionsaufrufen).

Zum Beispiel:

  • Nehmen wir an, Sie haben einen Namespace N
  • Nehmen wir an, Sie haben eine Klasse C , deklariert im Namensraum N ( mit anderen Worten, sein vollständiger Name lautet N::C )
  • Nehmen wir an, Sie haben eine Funktion F , deklariert im Namensraum N ( mit anderen Worten, sein vollständiger Name lautet N::F )
  • Nehmen wir an, die Funktion F hat unter seinen Parametern auch einen Parameter vom Typ C

... Dann N::F ist Teil von N::C öffentlichen Schnittstelle.

Namespaced-Funktionen haben, sofern sie nicht als "friend" deklariert sind, keinen Zugriff auf die Interna der Klasse, während statische Methoden das Recht haben, auf die Interna der Klasse zuzugreifen.

Das bedeutet zum Beispiel, dass Sie bei der Wartung Ihrer Klasse, wenn Sie die Interna Ihrer Klasse ändern müssen, nach Seiteneffekten in allen Methoden, einschließlich der statischen, suchen müssen.

Erweiterung I

Hinzufügen von Code zur Schnittstelle einer Klasse.

In C# können Sie einer Klasse auch dann Methoden hinzufügen, wenn Sie keinen Zugriff auf die Klasse haben. In C++ ist dies jedoch unmöglich.

Aber auch in C++ können Sie eine Funktion mit Namensraum hinzufügen, sogar zu einer Klasse, die jemand für Sie geschrieben hat.

Von der anderen Seite aus gesehen, ist dies wichtig, wenn Sie Ihren Code entwerfen, denn indem Sie Ihre Funktionen in einen Namensraum stellen, erlauben Sie Ihren Benutzern, die Schnittstelle der Klasse zu erweitern/vervollständigen.

Erweiterung II

Ein Nebeneffekt des vorherigen Punktes ist, dass es unmöglich ist, statische Methoden in mehreren Headern zu deklarieren. Jede Methode muss in derselben Klasse deklariert werden.

Bei Namespaces können Funktionen aus demselben Namespace in mehreren Headern deklariert werden (die fast standardmäßige Swap-Funktion ist das beste Beispiel dafür).

Erweiterung III

Das Schöne an einem Namespace ist, dass man ihn in manchem Code nicht erwähnen muss, wenn man das Schlüsselwort using :

#include <string>
#include <vector>

// Etc.
{
   using namespace std ;
   // Now, everything from std is accessible without qualification
   string s ; // Ok
   vector v ; // Ok
}

string ss ; // COMPILATION ERROR
vector vv ; // COMPILATION ERROR

Und Sie können die "Verschmutzung" sogar auf eine Klasse beschränken:

#include <string>
#include <vector>

{
   using std::string ;
   string s ; // Ok
   vector v ; // COMPILATION ERROR
}

string ss ; // COMPILATION ERROR
vector vv ; // COMPILATION ERROR

Dieses "Muster" ist obligatorisch für die korrekte Verwendung der nahezu standardisierten Swap-Sprache.

Und das ist mit statischen Methoden in Klassen nicht möglich.

C++-Namensräume haben also ihre eigene Semantik.

Aber es geht noch weiter, denn Sie können Namensräume ähnlich wie bei der Vererbung kombinieren.

Wenn Sie zum Beispiel einen Namespace haben A mit einer Funktion AAA einen Namensraum B mit einer Funktion BBB können Sie einen Namespace deklarieren C und bringen AAA y BBB in diesem Namespace mit dem Schlüsselwort using .

Sie können sogar den gesamten Inhalt eines Namespaces in einen anderen bringen, mit using namespace wie mit dem Namensraum D gezeigt!

namespace A
{
   void AAA();
   void AAA2();
}

namespace B
{
   void BBB();
}

namespace C
{
   using A::AAA;
   using B::BBB;
}

namespace D
{
   using namespace A;
   using namespace B;
}

void foo()
{
   C::AAA();
   // C::AAA2(); // ERROR, won't compile
   C::BBB();
}

void bar()
{
   D::AAA();
   D::AAA2();
   D::BBB();
}

Schlussfolgerung

Namespaces sind für Namespaces. Klassen sind für Klassen.

C++ wurde so konzipiert, dass jedes Konzept anders ist und in unterschiedlichen Fällen als Lösung für unterschiedliche Probleme verwendet wird.

Verwenden Sie keine Klassen, wenn Sie Namespaces benötigen.

Und in Ihrem Fall brauchen Sie Namespaces.

65voto

Dan Tao Punkte 121990

Es gibt viele Leute, die mir widersprechen würden, aber ich sehe das so:

Eine Klasse ist im Wesentlichen eine Definition für eine bestimmte Art von Objekt. Statische Methoden sollten Operationen definieren, die eng mit dieser Objektdefinition verbunden sind.

Wenn Sie nur eine Gruppe von zusammenhängenden Funktionen haben wollen, die nicht mit einem zugrunde liegenden Objekt verbunden sind oder Definition einer Art von Objekt dann würde ich sagen, nehmen Sie nur einen Namespace. Nur für mich, konzeptionell, das ist viel mehr sinnvoll.

Fragen Sie sich in Ihrem Fall zum Beispiel: "Was ist ein MyMath?" Wenn MyMath keine Art von Objekt definiert, dann I würde sagen: Mach keine Klasse daraus.

Aber wie gesagt, ich weiß, dass es viele Leute gibt, die mir in dieser Hinsicht (sogar vehement) widersprechen würden (insbesondere Java- und C#-Entwickler).

21voto

Shog9 Punkte 151504
  • Wenn Sie statische Daten benötigen, verwenden Sie statische Methoden.
  • Wenn es sich um Template-Funktionen handelt und Sie einen Satz von Template-Parametern für alle Funktionen zusammen angeben möchten, verwenden Sie statische Methoden in einer Template-Klasse.

Andernfalls verwenden Sie Funktionen mit Namensräumen.


Um auf die Kommentare zu antworten: Ja, statische Methoden und statische Daten werden oft überstrapaziert. Deshalb habe ich auch nur zwei angeboten, zugehörige Szenarien, in denen sie meiner Meinung nach hilfreich sein können. Im konkreten Beispiel des Auftraggebers (eine Reihe von mathematischen Routinen) könnte er, wenn er die Möglichkeit haben wollte, Parameter zu spezifizieren - z. B. einen Kerndatentyp und eine Ausgabepräzision -, die für alle Routinen gelten würden, etwas wie folgt tun:

template<typename T, int decimalPlaces>
class MyMath
{
   // routines operate on datatype T, preserving at least decimalPlaces precision
};

// math routines for manufacturing calculations
typedef MyMath<double, 4> CAMMath;
// math routines for on-screen displays
typedef MyMath<float, 2> PreviewMath;

Wenn Sie das nicht brauchen, dann auf jeden Fall einen Namespace verwenden.

12voto

coppro Punkte 14158

Sie sollten einen Namespace verwenden, denn ein Namespace hat viele Vorteile gegenüber einer Klasse:

  • Sie müssen nicht alles in der gleichen Kopfzeile definieren
  • Sie müssen nicht alle Ihre Implementierungen in der Kopfzeile offenlegen
  • Sie können nicht using ein Klassenmitglied; Sie können using ein Namespace-Mitglied
  • Sie können nicht using class Allerdings using namespace ist nicht so oft eine gute Idee
  • Die Verwendung einer Klasse impliziert, dass es ein Objekt gibt, das erstellt werden muss, obwohl es in Wirklichkeit keines gibt.

Statische Mitglieder sind meiner Meinung nach sehr, sehr überbeansprucht. In den meisten Fällen sind sie nicht wirklich notwendig. Die Funktionen statischer Mitglieder sind wahrscheinlich besser als Funktionen mit Dateiumfang geeignet, und statische Datenmitglieder sind einfach globale Objekte mit einem besseren, unverdienten Ruf.

7voto

alfC Punkte 12070

Ich möchte andere Antworten zusammenfassen und ergänzen. Außerdem ist meine Perspektive in der Welt der reinen Kopfzeile.


Namespaces

Vorteile:

  • einfache Lösung für die Benennung von Hierarchien
  • sie tragen keine Semantik, so dass es einfacher ist, zu lesen
  • können in verschiedenen Dateien stehen (Header)
  • kann erweitert werden
  • ADL
  • kann eine Abkürzung definiert werden ( using ).
  • Spielt gut mit der Überlastung des Bedieners
  • Kann für das Branding verwendet werden (Sie können Ihren Code entwerfen und einen Namespace darüber legen, ohne viel zu tun)

Nachteile:

  • alles ist öffentlich
  • private Dinge brauchen einen unbenannten Namensraum, damit er nicht explizit ist
  • ADL (ja, manche Leute verachten die ADL)
  • kann erweitert werden (dies kann eine schlechte Sache sein, besonders in Kombination mit ADL, die Semantik von bestehendem Code kann sich durch die Erweiterung des Namensraums ändern)
  • die Funktionen müssen in der Reihenfolge ihrer Verwendung definiert (oder deklariert) werden

Klassen mit statischen Methoden

Vorteile:

  • können private Komponenten (Funktionen, Variablen) haben und sind ausdrücklich gekennzeichnet.
  • Klassen können befreundet werden
  • können typparametrisiert werden (Vorlagen)
  • können selbst Template-Parameter sein
  • kann instanziiert werden
  • können an Funktionen übergeben werden (statische Funktionen verhalten sich standardmäßig wie nicht-statische Methoden).
  • es ist einfacher, Muster zu finden und von Gruppen unabhängiger Funktionen auszugehen und sie in eine richtige Klasse zu konvertieren (eventuell mit nicht statischen Mitgliedern)
  • die Abhängigkeiten zwischen den Klassen sind klar definiert
  • Funktionen (die statische Methode) können in beliebiger Reihenfolge definiert werden

Nachteile:

  • Keine ADL
  • kann nicht verlängert werden
  • braucht überall das Schlüsselwort static (Gelegenheit, sich über die Sprache lustig zu machen)
  • ein Overkill, um das Benennungsproblem allein zu lösen. In diesem Fall ist es schwer zu lesen.
  • die Funktionen (statische Methoden) müssen immer qualifiziert werden ( myclassspace::fun ). Es gibt keine Möglichkeit, Abkürzungen zu deklarieren ( using ).
  • fast unbrauchbar für die Überlastung von Bedienern, dafür ist ein komplizierter Freundschaftsmechanismus erforderlich.
  • kann nicht für das Branding verwendet werden.
  • müssen Sie daran denken, es mit ; :)

Zusammenfassend lässt sich sagen, dass Klassen mit statischen Methoden bessere Code-Einheiten sind und mehr Meta-Programmierung ermöglichen. Abgesehen von ADL und einigen syntaktischen Eigenheiten können sie alle Funktionen von Namespaces replizieren, aber sie können manchmal ein Overkill sein.

Unternehmen wie Bloomberg bevorzugen Klassen gegenüber Namespaces. Wenn Sie ADL oder Operatorüberladung nicht mögen, sind Klassen mit statischen Methoden der richtige Weg.

IMO, wäre es schön, wenn Namespace und Klassen integriert werden, um zwei Seiten derselben Medaille zu werden. Zum Beispiel einen Namespace in der Sprache als Klasse identifizieren, wo die Methoden standardmäßig statisch sind. Und dann in der Lage sein, sie als Template-Parameter zu verwenden. Ich wäre mir nicht sicher, was man mit ADL machen sollte (vielleicht könnte man es auf symbolische Operatorfunktionen beschränken, z.B. operatorX, was die ursprüngliche Motivation für Operatorüberladung und ADL war)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X