Singleton - mit allg. Konstruktor?
-
Ich mach's kurz: Gegeben sei folgendes Singleton-Pattern:
class A { private: X& m_x; public: A(X& x) :m_x(x) {} }; class Singleton : public A { private: Singleton() {} Singleton(const Singleton&); Singleton& operator=(const Singleton&); public: ~Singleton() {} static Singleton& getInstance() { static Singleton s; return s; } };Soweit so gut. Mein Problem: Die Klasse A enthält als Member eine Referenz.
Referenzen müssen aber gleich initialisiert werden.
Der Default-Konstruktor von Singleton ruft zunächst den Default-Konstruktor von A auf. Und da beschwert sich der Compiler "uninitialized reference m_x".Meine Idee war:
Ich muss also in Singleton den allgemeinen Konstruktor von A erzwingen, deshalb habe ich folgendes probiert:class Singleton : public A { private: Singleton(X& x) :A(x) {} Singleton(const Singleton&); Singleton& operator=(const Singleton&); static Singleton m_s; public: ~TestS() {} static void createSingleton(X& x) { Singleton s(x); m_s = s; } static Singleton& getInstance() { return m_s; } };Dann wiederum gibt es einen Link-Fehler:
[Link error] undefined reference m_s
Wie ist die Lösung für dieses Problem?
-
Dir fehlt ein:
Singleton Singleton::m_s;in der Implementationsdatei. Das nützt dir allerdings überhaupt nicht, weil der relevante Konstruktor nicht existiert. Zumindest das klassische Singleton-Pattern ist ungeeignet, wenn bei der Konstruktion Argumente übergeben werden müssen.
-
Z2 schrieb:
Zumindest das klassische Singleton-Pattern ist ungeeignet, wenn bei der Konstruktion Argumente übergeben werden müssen.
Gibt es Alternativen?
Ich bin nicht zu sehr auf das Singleton-Pattern verbissen. Mir geht darum, dass ich an meheren Stellen im Code auf eine Instanz (immer die gleiche) der Klasse Singleton zugreifen muss. Diese sollte möglichst zentral gehalten werden, so dass nicht zuviel Abhängigkeiten entstehen.
-
Wenn du dich darauf einlassen willst, das Objekt manuell anzulegen und wieder zu löschen:
class foo { static foo *s_This; private: foo (const foo&); public: foo (irgendwelche argumente); ~foo(); static foo& Get(); };foo foo::s_This = 0; foo::foo (irgendwelche argumente) { if (s_This) throw std::runtime_error ("only once instance of foo allowed"); s_This = this; } foo::~foo() { s_This = 0; } foo foo::Get() { if (!s_This) throw std::runtime_error ("no instance of foo"); return *s_This; }Ungetestet, leicht vereinfacht und an einigen Stellen mehr Pseudo-Code als C++. Aber ich denke, das Prinzip sollte klar sein.
-
Nicht ganz.
Wie willst du im Konstruktor das "this" erzeugen?
foo::foo (irgendwelche argumente) { if (s_This) throw std::runtime_error ("only once instance of foo allowed"); s_This = this; }Wohlgemerkt - die Singleton erbt von einer Klasse, für die der allg. Konstruktor aufgerufen werden muss.
Und wie meinst du das:
foo foo::s_This = 0;Eine Zuweisung mit einem Rückgabewert?

Ok, du willst den Zeiger anfangs eben auf 0 setzen, aber das "Wie" und "Wo" du das machst kann ich nicht ganz nachvollziehen.
-
sly schrieb:
Nicht ganz.
Wie willst du im Konstruktor das "this" erzeugen?
this ist ein Zeiger auf das Objekt, in dem du dich gerade befindet. Das muß nicht extra erzeugt werden.
sly schrieb:
Wohlgemerkt - die Singleton erbt von einer Klasse, für die der allg. Konstruktor aufgerufen werden muss.
Mit "allg. Konstruktor" meinst du irgendeinen Konstruktor der Argumente übergeben bekommt, ja? Wo ist dann das Problem?
sly schrieb:
Und wie meinst du das:
foo foo::s_This = 0;Das ist eine statische Member-Variable, siehe auch Klasse foo (da steht sie noch mal drin). Fehlt nur leider ein *, Sorry! Richtig sieht es so aus:
foo *foo::s_This = 0;
-
Das ist eine statische Member-Variable, siehe auch Klasse foo (da steht sie noch mal drin)
foo *foo::s_This = 0;Ok, verstanden. Ich kannte bisher diese Syntax für statische Membervariablen noch nicht, deshalb meine Verwirrung.

Den Konstruktor versteh ich trotzdem noch nicht. Sorry wenn ich nochmal nachhaken muss:
Das Bedeutung von "this" kenne ich, jedoch bisher nur in Methoden. Und da bezeichnet es einen Zeiger auf das aufrufende Objekt.
In einem Konstruktor ist this jedoch ein Zeiger auf ein Objekt, das gerade erzeugt wird...und wie wurstle ich jetzt da den Parameteraufruf für den Konstruktor rein?
Mein Ziel ist es ja, den Konstruktor auf die Art wie:
Foo f(irgendwelche Argumente);
aufrzurufen.
Aber ich kann doch in einem Konstruktor den Konstruktor nicht slebst wieder rekursiv aufrufen -> Verwirrung pur.
Ich steh auf'm Schlauch, wäre nett wenn du den Konstruktor vollständig implementierst (denn da fehlt ja noch was nehm ich an...?)
foo::foo (irgendwelche argumente) { if (s_This) throw std::runtime_error ("only once instance of foo allowed"); s_This = this; }
-
Der Konstruktor ist vollständig, abgesehen von der initialisierung der Basisklasse
: basisklasse (irgendwelche argumente)Vielleicht sollte ich dir lieber mal ein Anwendungsbeispiel geben.
Du kannst dein foo-Objekt zum Beispiel in der main anlegen:
int main() { // irgendwas foo Foo (irgendwelche argumente); // irgendwas2 }Von dem Zeitpunkt an, an dem Foo konstruiert worden ist, kannst du somit überall mittels foo:Get() auf diese Instanz zugreifen.
-
Ok ok ok...ich glaub der Groschen ist gefallen: im Konstruktor fehlt nur ein:
foo::foo (irgendwelche argumente) :BasisKlasse(irgendwelche Argumente)
{
....
}
Jedenfalls funktioniert es jetzt so...sag mir bitte jetzt nicht, dass das falsch ist.

-
Zeitgleich
