static und abgeleitete Klasse
-
Hallo,
ich brauche sowas hier (was ja logischerweise nicht geht)
class IData { ... }; class IFactory { public: static IData* CreateData() = 0; }; class AData : public IData { ... }; class AFactory : public IFactory { public: static IData* CreateData() { return new AData(); } };Später möchte ich nur IFactory::CreateData() aufrufen, um das spezifische Data-Objekt zu bekommen.
Ich dachte man könnte das über Templates lösen, was aber im Nachhinein Quatsch ist
weil man dort auch den richtigen Klassennamen angeben muss.template <class Derived> class IFactory { public: static IData* CreateData() { return Derived::CreateData(); } }; class AFactory : public IFactory<AFactory> { public: static IData* CreateData() { return new AData(); } }; //AData *data = (AData*)IFactory<AFactory>::CreateData();Hat jemand eine Idee, wie ich das Problem lösen kann?
greetz KN4CK3R
-
KN4CK3R schrieb:
Hat jemand eine Idee, wie ich das Problem lösen kann?
Wenn du Vererbung benutzen willst bei den Factories, dann musst du davon eh ein Objekt haben, um es zu nutzen, dann kannst du auch das static weglassen und die create-Funktion zu einer ganz normalen virtuellen Methode machen.
Ich weiß allerdings nicht so genau, was du damit erreichen willst. Vielleicht erklärst du es uns, dann kann man dir auch eine passende Lösung vorschlagen. Möchtest du zur Laufzeit die Factory austauschen können?
-
IData ist in meinem Fall eigentlich IFont und ein Interface zu einer Schriftklasse. IFactory ist IRenderer, das einen Renderer darstellt (OpenGL, DirectX, etc).
Ich brauche nun in einer weiteren Klasse die Möglichkeit eine Schrift erstellen zu lassen. Da man im fertigen Programm einfach den Renderer wechseln können soll, brauche ich nun eine Möglichkeit, dass wenn zB der OpenGL Renderer verwendet wird, mir an dieser Stelle eine OpenGL-Font zurückgegeben wird usw.Ich glaube eine mehr oder weniger elegante Möglichkeit wäre das ganze so in der Art zu lösen:
class IRenderer { private: static IRenderer* instance; public: static IFont* CreateFont() { return (IFont*)instance->CreateFont(); } }; class OpenGLRenderer : public IRenderer { public: OpenGLRenderer() { instance = this; } OpenGLFont* CreateFont() { ... } };Wobei zumindest eine Instanz eines Renderers angelegt sein worden muss, was auch irgendwie doof ist.
greetz KN4CK3R
-
KN4CK3R schrieb:
IData ist in meinem Fall eigentlich IFont und ein Interface zu einer Schriftklasse. IFactory ist IRenderer, das einen Renderer darstellt (OpenGL, DirectX, etc).
Dann sag das doch gleich

Ich glaube eine mehr oder weniger elegante Möglichkeit wäre das ganze so in der Art zu lösen:
Nein, das ist nicht elegant. nicht weniger und schon garnicht mehr. Vor allem tut es nicht was du willst, offenbar hast du es nichtmal ausprobiert.
Wobei zumindest eine Instanz eines Renderers angelegt sein worden muss, was auch irgendwie doof ist.
Das muss es so oder so, siehe mein voriger Post.
Da man im fertigen Programm einfach den Renderer wechseln können soll, brauche ich nun eine Möglichkeit, dass wenn zB der OpenGL Renderer verwendet wird, mir an dieser Stelle eine OpenGL-Font zurückgegeben wird usw.
Dann speichere irgendwo einen Pointer auf einen abstrakten Renderer und hänge genau den Renderer darunter, den du verwenden willst. Für die Verwendung von (Laufzeit-) Polymorphie und virtuellen Funktionen kommst du da nicht rum.
Google mal nach dem Stichwort "abstract factory pattern"
-
Ist total egal wie dein Design aussieht. Hier rennen so viele Superschlaue rum, die dein Design IMMER kritisieren werden. Denn die wissen eh generell alles besser.
-
getestet hab ich den Code nicht, da es mir mehr ums theoretische ging.
Die abstract factory hab ich mir angeschaut, aber das ist ja das was ich eh schon hab, wenn ich das static weglasse.
Also läuft alles darauf hinaus, dass der Anwender zwingend am Anfang einen globalen Renderer initialisieren muss?greetz KN4CK3R
-
Also läuft alles darauf hinaus, dass der Anwender zwingend am Anfang einen globalen Renderer initialisieren muss?
Ja ... weil bei der initialisierung legt er doch spaeter wirdklich fest, welche renderer genommen werden ...
Generell kann man "dein Thema" auf 2 wege Loesen.
1. ueber Polymorphie:
Die legst beim init, wahrscheinlich anhand von parametern (ini datei oder ähnliches) fest, welcher renderer verwendet wird. Dazu sind instanzen notwendig, weil die die Vtable brauchen um auf die richtigen Typen zu schiessen.
Dein virtueller Construktor, aka Factory Methode duerfen also nicht statisch sein.Vorteil: dein Programm kann dynamisch, also mit ein und den selben binaries, zwischen den renderern umgeschaltet werden.
Nachteil: Polymorphie verhindert oft genug stack objecte und inlining. Ist dadurch grad beim erzeugen von objecten langsamer, bzw bietet keine "performanten" Loesungen an.2. Generizitaet (Templates)
Du legst irgendwo statisch, also im header ueber nen typedef fest, auf welche typen das schiesst.
Deine Objecte sind dann meist auch nicht abgeleitet, sondern benutzen nur die selben Signaturen, und sind dadurch "generisch". Also du tpyisierst alle deine Objecte vorher per templates.Vorteil: Stack-Objecte laessen sich oefters verwenden, performanter.
Nachteil: binaries sind nur fuer einen bestimmten Pfad gebaut. d.h. du schaltest mit dem kompilieren bzw der Distributierung zwischen den Rendern um.Kann man meist entschaerfen, in dem man, den Renderer in dem Falle, komplett in eine dll auslagert, und fuer jeden Render seine eigene dll baut. Die app zieht dann dynamisch die entsprechende dll an, dann funktioniert die dynamische konfiguration wieder.
Das dll interface muss aber dann auch binaer fest definiert / sein ... was meist in nen heftiges interface ausartet ... je nach funktionsumfang.Du musst selber entscheiden was dir lieber ist.
Einsteiger tun sich mit polymorphie oft leichter ... templates koennen schon ziemlich knoten ins Hirn machen
Ciao ...
-
ok, dann danke ich euch mal bis dahin und werde denk ich den Weg über Polymorphie wählen, passt besser zum bisherigen Stand der Dinge.
greetz KN4CK3R
-
RHBaum schrieb:
Nachteil: Polymorphie verhindert oft genug stack objecte
Polymorphie hat nur leider absolut gar nichts mit dem Stack zu tun, die funktioniert da auch. Das Problem, welches du meinst, nennt sich Slicing.
-
Rein theoretisch kannst du natürlich auch deiner Renderer-Klasse eine statische create-Funktion und einen statischen Funktionspointer verpassen, den diese Funktion dann aufruft. so kannst du zur Laufzeit die tatsächliche create-Funktion dann auswechseln:
class IRenderer { private: // -- // instance brauchst du nicht, um eine statische Funktion aufzurufen typedef IFont *FontCreator(); static FontCreator creator; public: static IFont* createFont() { return creator(); } static void setCreator(FontCreator c) { creator = c; } }; IFont* createOpenGLFont() { return new OpenGLFont; } // ... int main() { IRenderer::setCreator(&createOpenGLFont); IFont* pfont = IRenderer::createFont(); if (dynamic_cast<ObenGLFont*>(pfont)) { cout << "Got it!"; } }Kann man natürlich noch erweitern, mit Vor- und Nachbearbeitung unabhängig von der Erzeugerfunktion, std::function statt der Funktionspointer, deleter-Funktion usw. Hab da mal vor ein paar Wochen ein entsprechend frei parametrisierbares Template gebastelt...
-
findet sich das Template hier irgendwo im Forum?
greetz KN4CK3R