Designfrage: Alternative für Init-Methode (Antwort ist nicht der Konstruktor!)
-
Hi,
angenommen ich habe eine Klasse mit einer statischen Variable. Der genaue Wert davon ist aber erst zur Laufzeit bekannt. Die Instanzen der Klasse brauchen einen vernünftig initialisierten Wert, um ihre Aufgabe zu erfüllen. Ungefähr so:
class foo{ private: static komplizierter_datentyp static_variable; public: void leg_los() { // Benötigt static_variable } static void init(/*Parameter*/) { // Initialisiert static_variable; } };Kann man den Benutzer der Klasse irgendwie dazu zwingen, die init-Methode vor dem ersten leg_los() aufzurufen? Vorzugsweise mit Fehlermeldung zur Compilezeit? Ein assert zur Laufzeit wäre nur insofern hilfreich, als das es eine genauere Fehleranalyse erlaubt. Das Programm wird nämlich höchstwahrscheinlich ohnehin an der Stelle abstürzen, wenn ein uninitialisierter Wert benutzt wird.
Die beste Möglichkeit, die mir dazu einfällt, wäre ein Auslagern der Variable:
class foo{ public: void leg_los(komplizierter_datentyp ¶meter) { // Benutzt Parameter } };Dies stellt sicher, dass der benötigte Parameter initialisiert ist, da man die Funktion ohne gar nicht aufrufen könnte. aber kleiner Nachteil: Jetzt muss man sich als Benutzer um mehr kümmern. Im schlimmsten Fall muss man das ehemalige statische Objekt nun im ganzen Programm rumreichen oder gleich global machen. Das ist zwar erstmal Problem des Nutzers, dennoch finde ich dies unschön (vor allem da dieser Benutzer erstmal ich selbst bin
).
Es ist auch von der Modellierung nur halbschön, denn die statische Eigenschaft habe ich da anfangs nicht hingesetzt weil es den Code kürzer macht, sondern weil es sich auch vom Modell her um eine gemeinsame Eigenschaft aller Instanzen dieser Klasse handelt.Gibt's da bessere Alternativen?
-
Also ich mache so was so, das ich dann halt die Init-Methode in der anderen Methode explizit aufrufe. Damit das aber nicht unnötig passiert, merke ich mir, ob ich es schon aufgerufen habe. Bsp.:
class foo{ private: static komplizierter_datentyp static_variable; static bool initilized; public: void leg_los() { init(); // Benötigt static_variable } static void init(/*Parameter*/) { if(initilized) return; // Initialisiert static_variable; } };
-
Warum nicht so? Immer diese kompliziert denkenden C++ler...

private: static komplizierter_datentyp& init() { static komplizierter_datentyp var = ...; return var; } public: void leg_los() { verwende(init()); }
-
SeppJ schrieb:
Kann man den Benutzer der Klasse irgendwie dazu zwingen, die init-Methode vor dem ersten leg_los() aufzurufen? Vorzugsweise mit Fehlermeldung zur Compilezeit?
Ich glaub kaum, dass der Compiler immer die Aufrufreihenfolge herrausfinden kann, ich denke sogar das ist unmöglich.
Das kommentierte /*Parameter*/ soll bedeuten, dass init schon Parameter braucht, aber leg_los nicht?
-
Nexus schrieb:
Warum nicht so?
private: static komplizierter_datentyp& init() { static komplizierter_datentyp var = ...; return var; } public: void leg_los() { verwende(init()); }
...wobei die statische Funktion nicht unbedingt inline sein muss...
-
krümelkacker schrieb:
Nexus schrieb:
Warum nicht so?

Gefällt mir ebenfalls.

Hast recht mit dem kompliziert denken.
Das mit dem /*Parameter*/ heißt, dass es Parameter geben könnte.
-
Gut, dass es euch gefällt

krümelkacker schrieb:
...wobei die statische Funktion nicht unbedingt inline sein muss...
Ja, war mehr der Einfachheit halber. Ausser bei Templates habe ich eigentlich nie Inline-Methoden, und selbst da halte ich die Definition oft von der Deklaration getrennt.
SeppJ schrieb:
Hast recht mit dem kompliziert denken.
Glaub mir, das passiert mir selbst oft genug.

Kürzlich hatte ich zwei relativ ähnliche Klassen, für die ich Code in ein separates (CRTP-)Template auslagern wollte, um Codeduplikation zu vermeiden. Das artete schnell in eine Template-Orgie mit etlichen Parametern aus, sodass ich nachher doppelt so viel Code hatte und merkte, dass doch nicht alles so einheitlich geht. Naja, inzwischen dupliziere ich halt ein paar Zeilen Code.

-
SeppJ schrieb:
Das mit dem /*Parameter*/ heißt, dass es Parameter geben könnte.
Wenn es Parameter gibt, aber leg_los keine hat, dann ist das von Nexus auch nicht toll bzw. geht nicht. Da würde ich deine erste Variante verwenden und ein assert in leg_los.
-
Da das mit den Parametern echt nicht gut zu Nexus Vorschlag passte, habe ich mir jetzt folgendes gedacht:
class komplexer_datentyp { public: komplexer_datentyp(/*Parameter*/); leg_los(foo& f); };Das stellt sicher, dass die statischen Daten existieren, da man ein Objekt davon braucht um leg_los überhaupt aufrufen zu können. Ist zwar nicht viel anders als die Lösung die ich zuerst hatte, aber flexibler und passt (in meinem Fall) besser zum Modell. Wie gesagt sind foo und komplexer_datentyp ohnehin modelltechnisch eng zusammengehörig. Die Trennung ob komplexer_datentyp eine Eigenschaft von foo ist oder ob komplexer_datentyp weiß wie man ein foo loslegen lässt ist da nicht eindeutig beantwortbar, daher kann ich die technisch einfachere Lösung wählen.
Man könnte sogar beides mischen, indem man komplexer_datentyp::leg_los() einfach f.leg_los() mit this als Parameter aufrufen lässt.
-
wie wäre es mit einem static_assert?

-
123454312 schrieb:
wie wäre es mit einem static_assert?

Gerne, aber wie? Ich glaube nicht, dass das in diesem Fall geht.