oop-trainer.de
Ralf Schneeweiß

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 Singleton­klasse, 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 Makro­implementierung 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 Definitions­makros 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 problema­tisches 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.




Zum Anfang des Dokuments.