Eine Implementierung des Singleton-Musters in C++
Ein Vorschlag
In diesem Artikel soll ein Implementierungsvorschlag für das Singleton-Muster
vorgestellt werden, ohne dabei auf die verschiedenen möglichen Ausprägungen
des Musters einzugehen.
class Singleton
{
public:
static Singleton& exemplar();
private:
Singleton() {}
Singleton( const Singleton& );
};
Singleton& Singleton::exemplar()
{
static Singleton instanz;
return instanz;
}
|
Listing 1: Singleton mit statischer Instanz
|
Eine Diskussion des Musters befindet sich in dem Artikel
„Das Singleton Muster in C++“,
der die strukturellen Probleme einer Implementierung beleuchtet.
Ausgehend von einer vollständigen Singletonklasse, wie sie in dem genanten
Artikel beschrieben ist, geht es nun darum, eine Form der Implementierung zu
entwickeln, die jede beliebige Klasse möglichst einfach zu einer Singletonklasse
machen kann.
Aufbauend auf den Vorüberlgungen, die in dem vorher genannten Artikel angestellt
wurden, wird eine Implementierung unter Verwendung von Makros gewählt. Dabei sollen
alle Elemente, die eine Singletonklasse ausmachen - privater Konstruktor, statische Instanzmethode -,
in die Klasse eingeschleust werden.
class KlasseX
{
DECLARE_SINGLETON(KlasseX);
};
DEFINE_SINGLETON(KlasseX);
|
Listing 2: Einfache Anwendung von Makros
|
Das Hauptaugenmerk soll dabei auf einer möglichst einfachen Anwendung und einer guten Beherrschbarkeit liegen,
wie es in Listing 2 ansatzweise demonstriert ist.
Es bietet sich eine einfache Makroimplementierung an, wie sie in Listing 3 gezeigt wird.
Es wird nicht näher auf die Syntax der Makrodefinition in C++ eingegangen und vorausgesetzt,
dass der Leser die Makrosyntax kennt oder sich der entsprechenden Literatur bedienen kann.
Prinzipiell beruht diese Technik einfach auf Textersetzung ohne Typenprüfung.
Es gibt ähnliche Implementierungen, die sich der Template-Programmierung in C++ bedienen.
Diese Implementierungen gehen in vielen Fällen über den Vorschlag dieses Textes hinaus,
ziehen aber meistens die Notwendigkeit nach sich, den Kontruktor der Klasse öffentlich zu machen.
#define DECLARE_SINGLETON( CLASSNAME ) \
public: \
static CLASSNAME & exemplar(); \
private: \
CLASSNAME() {} \
CLASSNAME(const CLASSNAME &);
#define DEFINE_SINGLETON( CLASSNAME ) \
CLASSNAME & CLASSNAME##::exemplar() \
{ \
static CLASSNAME instanz; \
return instanz; \
}
|
Listing 3: Die Implementierng der Makros
|
Zurück zur Implementierung: Die Makros brauchen den Klassennamen als Parameter, um die Konstruktoren, die Instanziierungsmethode
und die statische Instanz zu definieren. Nun können sie für jede beliebige Klasse angewendet werden.
Dabei stellt die gewählte Implementierung einige Bedingungen für die Anwendung:
erstens muss die Anwendung des Definitionsmakros in einer CPP-Datei stehen und nicht in einem Header. Zweitens muss
dem Entwickler bekannt sein, dass er durch die Verwendung des Makros eine Funktion exemplar()
bekommt,
mit der er die Instanz des Singletons abrufen kann. Das sind zwar Vorbedingungen, die die Implementierung
nicht besonders elegent machen, dafür aber einigermaßen sicher. Schließlich ist das Singleton ein sehr
problematisches Muster und es kann nicht oft genug darauf hingewiesen werden, dass dessen Einsatz häufig
mehr Problemfelder eröffnet, als es zu beseitigen hilft.
Es gibt viele solche Vorschläge zur Implementierung von Singletons. Wichtige Überlegungen
können die Lebensdauer, bzw. den genauen Zeitpunkt der Objektzerstörung betreffen.
Wer sich für weiterführede Literatur interessiert, dem sei die Publikation von
Andrei Alexandrescu „Modern C++ Design. Generic Programming and Design Patterns Applied.“ empfohlen.
Ralf Schneeweiß - 29. September 2003
Literatur
[GoF95] | Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides: Design Patterns. Elements of Reusable Objectoriented Software. 1995. |
[JV99] | John Vlissides: Entwurfsmuster anwenden. 1999. |
[AA01] | Andrei Alexandrescu: Modern C++ Design. Generic Programming and Design Patterns Applied. 2001. |
Anhang
Die gezeigte Implementierung wurde mit den folgenden Compilern getestet:
- Borland C++ 5.5.1
- MS Visual C++ 6.0
- Digital Mars C++ 8.29n
- GNU C++ 3.3.1
Trotz sorgfältigen Testens geschieht ein Einsatz der gezeigten Implementierung
in Software in eigener Verantwortung und auf eigene Gefahr. Der Autor übernimmt
keinerlei Verantwortung für mögliche Fehlfunktionen, die im Zusammenhang
mit dem Einsatz der beschriebenen Singletonimplementierung auftreten.