it-swarm.com.de

C++ - Singleton vs. vollständig statisches Objekt

Nehmen wir an, wir brauchen nur eine Instanz einer Klasse in unserem Projekt. Dafür gibt es mehrere Möglichkeiten.

Ich möchte vergleichen Bitte können Sie mein Verständnis überprüfen.

1) Klassisches Singleton-Muster

2) Vollständig statische Klasse (alle Methoden und Member sind statisch).


Soweit ich weiß, sind die Unterschiede folgende:

a) Die Reihenfolge der Initialisierung statischer Member zwischen verschiedenen Einheiten ist nicht definiert. Die Initialisierung vollständig statischer Member kann also keine statischen Member/Funktionen aus anderen Modulen verwenden. Und Singleton hat dieses Problem nicht.

b) Wir müssen uns mit Threading für getInstance () von Singleton befassen. Eine vollständig statische Klasse hat dieses Problem jedoch nicht.

c) Der Zugriff auf Methoden sieht etwas anders aus. Foo :: bar (); vs Foo :: getInstance () -> bar (); Im Allgemeinen kann Singleton NULL zurückgeben, um zu ermitteln, dass Probleme mit der Konstruktion von Objekten aufgetreten sind und die statische Klasse nicht.

d) Die Definition der Klasse wirkt mit statischen Klassen für statische Klassen etwas unbeholfen.

Habe ich etwas verpasst? 

32
Victor Ronin

Egal, ob Sie es Singleton oder Monostate oder einen ausgefallenen Namen nennen ... die sehr ärgerliche Natur besteht darin, dass Sie EINE Instanz des Objekts und viele Schreibvorgänge haben: Globale Variablen, egal in welcher Form, sind evil .

Die Idee, dass Sie need eine eindeutige Instanz sind, ist im Allgemeinen unübersichtlich. Meistens benötigen Sie Teile, die kommunizieren, die gleiche Instanz. Eine andere Gruppe von Teilen könnte jedoch problemlos eine andere Instanz ohne Probleme verwenden.

Jeder Code, der need eine globale Variable beansprucht, ist höchst verdächtig. Es mag einfacher erscheinen, eine zu verwenden, aber seien wir ehrlich, man könnte das Objekt perfekt an jede Funktion übergeben, die Signatur würde kompliziert werden, aber es würde trotzdem funktionieren.

Ich gebe zu, es scheint einfacher zu sein, globale Variablen zu verwenden ... bis Sie die Probleme bemerken:

  • Multithreading ist gefährdet
  • Testbarkeit ist reduziert, da ein Test den folgenden Test beeinflussen kann
  • die Abhängigkeitsanalyse ist äußerst kompliziert: Es ist schwer zu wissen, von welchem ​​Status Ihre Methode abhängt, wenn Sie globale Submethoden aus dem System ziehen ...

Soweit Singleton betroffen ist, ist die Multithread-Erstellung in C++ vor C++ 0x nicht möglich (wenn statische Locals verwendet werden können). Daher müssen Sie sie nur in einem Thread erstellen und den Zugriff verzögern, bevor Sie sie in main instanziieren Es ist deine beste Wette.

Zerstörung kann Chaos verursachen, da das Leben des Singleton/Static enden kann, bevor andere damit fertig sind, und dann ist es undefiniertes Verhalten. Dies ist typisch für einen Logger-Singleton. Die übliche Strategie ist, schamlos auszulaufen ...

Wenn Sie danach noch eine haben möchten, wünsche ich Ihnen viel Glück. Das ist alles, was diese Community für Sie tun kann.

20
Matthieu M.

Eine weitere Option, die Sie übersehen, ist namespace.

namespace xyz {
namespace {
    int private_variable;
}

int get_pv() {
    return private_variable;
}
}

Funktionell wird dies Ihrer Option # 2 ähneln, aber Sie können dieses Ding nicht versehentlich "löschen". Sie können nicht versehentlich eine Instanz davon erstellen. Es handelt sich lediglich um eine Sammlung von zugehörigen, global zugänglichen Daten und Funktionen. Sie können (wie in meinem Beispiel) sogar "private" Mitglieder und Funktionen haben.

Natürlich würde die Verwendung so aussehen:

int x = xyz::get_pv();
9
Evan Teran

Nehmen wir an, wir brauchen nur eine Instanz einer Klasse in unserem Projekt. Dafür gibt es mehrere Möglichkeiten.

Eine bessere Lösung:

Eine Variable in main, die Sie als Parameter an alle erforderlichen Funktionen übergeben, wäre eine andere.

a) Die Reihenfolge der Initialisierung statischer Member über verschiedene Einheiten hinweg ist nicht definiert. Bei der Initialisierung vollständig statischer Member können also keine statischen Member/Funktionen aus anderen Modulen verwendet werden. Und Singleton hat dieses Problem nicht.

Singletons haben dieses Problem, wenn der Konstruktor/Destruktor auf eine andere globale statische Lebensdauervariable zugreift.

b) Wir müssen uns mit Threading für getInstance () von Sigleton befassen. Eine vollständig statische Klasse hat dieses Problem jedoch nicht.

Nicht wirklich ein Problem ist es? Wenn Sie sich dessen bewusst sind, fügen Sie einfach die entsprechenden Sperren in den Code ein.

c) Der Zugriff auf Methoden sieht etwas anders aus. Foo :: bar (); vs Foo :: getInstance () -> bar (); Im Allgemeinen kann sigleton NULL zurückgeben, um zu ermitteln, dass beim Erstellen des Objekts einige Probleme aufgetreten sind, und die statische Klasse kann nicht.

Ich würde machen, dass getInstance () eine Referenz zurückgibt. Dann gibt es keine Mehrdeutigkeit, wenn der Zeiger NULL ist. Es hat entweder funktioniert oder eine Ausnahme ausgelöst. Dies führt auch zu einem Design, bei dem die Zerstörung in der Instanz korrekt ist (nicht als Ratschlag für Singleton verwenden. Ich würde es vermeiden, wenn möglich (aber wenn Sie es ordentlich machen)).

d) Die Definition der Klasse wirkt mit statischen Klassen für statische Klassen etwas unbeholfen.

Es ist nicht klüger als ein Singleton richtig zu schreiben.

Das Problem bei diesen beiden Methoden ist, dass beide auf global mutable state zugreifen und die Verwendung dieser 'Einzelinstanz'-Objekte durch andere Objekte für den Benutzer verborgen ist. Dies kann zu Problemen beim Testen führen (TDD erfordert die Möglichkeit, externe Funktionalität zu simulieren, aber global mutable state verhindert, dass der Tester externe Abhängigkeiten (leicht) spottet).

Jedes Objekt, das kein POD ist, verfügt über einen Konstruktor, der möglicherweise eine Ausnahme auslösen kann. Für Objekte im globalen Namespace bedeutet dies, dass Ausnahmen ausgelöst werden können, bevor main () eingegeben wird (dies kann zu schwer zu findenden Fehlern führen (wenn Sie viele globale Objekte haben (Sie müssen überall Haltepunkte setzen)) Dasselbe Problem besteht bei einem Singleton, das faul bewertet wird. Wenn es beim ersten Gebrauch geworfen wird, wie korrigiert man das, damit ein nachfolgender Versuch nicht ausgelöst wird? Wird Ihre Anwendung bei jedem Abrufen des Singleton weiterworfen?

1
Martin York

Sie könnten hinzufügen: Statische Objekte können Ausnahmen auslösen. Die ausführbare Datei wird nicht gestartet und es ist schwierig, sie gut zu debuggen.

0
Jay

Sie können auch das Borg-Muster verwenden, das in C++ etwas komplizierter ist, sofern Sie nicht auf eine gemeinsam genutzte Zeigerklasse zugreifen können. Die Idee ist, dass eine beliebige Anzahl von Borg-Klassen instanziiert werden kann, der Status jedoch für alle Instanzen gemeinsam ist.

0
Colin