Betrachten Sie die Lösung von Matt Price .
- In C ++ hat eine "statische Klasse" keine Bedeutung. Das nächste ist eine Klasse mit nur statischen Methoden und Mitgliedern.
- Die Verwendung statischer Methoden schränkt Sie nur ein.
Was Sie wollen, ist, ausgedrückt in C ++ - Semantik, Ihre Funktion (denn es ist eine Funktion) in einen Namespace zu setzen.
Bearbeiten 2011-11-11
In C ++ gibt es keine "statische Klasse". Das nächste Konzept wäre eine Klasse mit nur statischen Methoden. Beispielsweise:
// header
class MyClass
{
public :
static void myMethod() ;
} ;
// source
void MyClass::myMethod()
{
// etc.
}
Sie müssen sich jedoch daran erinnern, dass "statische Klassen" Hacks in Java-ähnlichen Sprachen (z. B. C #) sind, die keine Nichtmitgliedsfunktionen haben können. Sie müssen sie stattdessen als statische Methoden in Klassen verschieben.
In C ++ möchten Sie wirklich eine Nichtmitgliedsfunktion, die Sie in einem Namespace deklarieren:
// header
namespace MyNamespace
{
void myMethod() ;
}
// source
namespace MyNamespace
{
void myMethod()
{
// etc.
}
}
Warum ist das so?
In C ++ ist der Namespace für das Muster "Statische Java-Methode" leistungsfähiger als Klassen, weil:
- statische Methoden haben Zugriff auf die privaten Symbole der Klassen
- Private statische Methoden sind weiterhin für alle sichtbar (wenn nicht zugänglich), was die Kapselung etwas verletzt
- statische Methoden können nicht vorwärts deklariert werden
- statische Methoden können vom Klassenbenutzer nicht überladen werden, ohne den Bibliotheksheader zu ändern
- Es gibt nichts, was mit einer statischen Methode ausgeführt werden kann, die nicht besser ausgeführt werden kann als eine (möglicherweise befreundete) Nichtmitgliedsfunktion im selben Namespace
- Namespaces haben ihre eigene Semantik (sie können kombiniert werden, sie können anonym sein usw.)
- etc.
Schlussfolgerung: Kopieren Sie das Java / C # -Muster nicht in C ++. In Java / C # ist das Muster obligatorisch. Aber in C ++ ist es ein schlechter Stil.
Bearbeiten 2010-06-10
Es gab ein Argument für die statische Methode, da manchmal eine statische private Mitgliedsvariable verwendet werden muss.
Ich bin etwas anderer Meinung, wie unten gezeigt:
Die "Static Private Member" -Lösung
// HPP
class Foo
{
public :
void barA() ;
private :
void barB() ;
static std::string myGlobal ;
} ;
Erstens heißt myGlobal myGlobal, da es immer noch eine globale private Variable ist. Ein Blick auf die CPP-Quelle verdeutlicht Folgendes:
// CPP
std::string Foo::myGlobal ; // You MUST declare it in a CPP
void Foo::barA()
{
// I can access Foo::myGlobal
}
void Foo::barB()
{
// I can access Foo::myGlobal, too
}
void barC()
{
// I CAN'T access Foo::myGlobal !!!
}
Auf den ersten Blick scheint die Tatsache, dass die freie Funktion barC nicht auf Foo :: myGlobal zugreifen kann, aus Sicht der Kapselung eine gute Sache zu sein ... Es ist cool, weil jemand, der sich das HPP ansieht, nicht darauf zugreifen kann (es sei denn, er greift auf Sabotage zurück) Foo :: myGlobal.
Wenn Sie es sich jedoch genau ansehen, werden Sie feststellen, dass es sich um einen kolossalen Fehler handelt: Nicht nur Ihre private Variable muss im HPP deklariert sein (und somit für die ganze Welt sichtbar sein, obwohl sie privat ist), sondern Sie müssen sie auch deklarieren im selben HPP alle (wie in ALLEN) Funktionen, die berechtigt sind, darauf zuzugreifen !!!
Wenn Sie also ein privates statisches Mitglied verwenden, gehen Sie nackt mit der Liste Ihrer Liebenden, die auf Ihre Haut tätowiert sind, nach draußen: Niemand darf berühren, aber jeder kann einen Blick darauf werfen. Und der Bonus: Jeder kann die Namen derjenigen haben, die berechtigt sind, mit Ihren Geheimnissen zu spielen.
private
in der Tat ... :-D
Die Lösung "Anonyme Namespaces"
Anonyme Namespaces haben den Vorteil, dass sie Dinge wirklich privat machen.
Zunächst der HPP-Header
// HPP
namespace Foo
{
void barA() ;
}
Nur um sicherzugehen, dass Sie bemerkt haben: Es gibt keine nutzlose Erklärung von barB oder myGlobal. Das bedeutet, dass niemand, der den Header liest, weiß, was sich hinter barA verbirgt.
Dann ist der CPP:
// CPP
namespace Foo
{
namespace
{
std::string myGlobal ;
void Foo::barB()
{
// I can access Foo::myGlobal
}
}
void barA()
{
// I can access myGlobal, too
}
}
void barC()
{
// I STILL CAN'T access myGlobal !!!
}
Wie Sie sehen, können fooA und fooB wie die sogenannte "static class" -Deklaration weiterhin auf myGlobal zugreifen. Aber sonst kann niemand. Und niemand außerhalb dieses CPP weiß, dass fooB und myGlobal überhaupt existieren!
Im Gegensatz zu der "statischen Klasse", die nackt mit ihrem auf die Haut tätowierten Adressbuch läuft , ist der "anonyme" Namespace vollständig bekleidet , was AFAIK besser zu verkapseln scheint.
Ist es wirklich wichtig?
Es sei denn, die Benutzer Ihres Codes sind Saboteure (ich lasse Sie als Übung herausfinden, wie man mit einem schmutzigen, nicht definierten Hack auf den privaten Teil einer öffentlichen Klasse zugreifen kann ...), was private
ist private
, auch wenn es so ist ist im private
Abschnitt einer Klasse sichtbar, die in einem Header deklariert ist.
Wenn Sie jedoch eine weitere "private Funktion" mit Zugriff auf das private Mitglied hinzufügen müssen, müssen Sie sie dennoch der ganzen Welt deklarieren, indem Sie den Header ändern. Dies ist für mich ein Paradoxon: Wenn ich die Implementierung von ändere Mein Code (der CPP-Teil), dann sollte sich die Schnittstelle (der HPP-Teil) NICHT ändern. Zitat von Leonidas: " Das ist ENCAPSULATION! "
Bearbeiten 20.09.2014
Wann sind statische Klassenmethoden tatsächlich besser als Namespaces mit Nicht-Member-Funktionen?
Wenn Sie Funktionen gruppieren und diese Gruppe einer Vorlage zuordnen müssen:
namespace alpha
{
void foo() ;
void bar() ;
}
struct Beta
{
static void foo() ;
static void bar() ;
};
template <typename T>
struct Gamma
{
void foobar()
{
T::foo() ;
T::bar() ;
}
};
Gamma<alpha> ga ; // compilation error
Gamma<Beta> gb ; // ok
gb.foobar() ; // ok !!!
Denn wenn eine Klasse ein Vorlagenparameter sein kann, können Namespaces dies nicht.