Ich habe eine Datei: Base.h
class Base;
class DerivedA : public Base;
class DerivedB : public Base;
/*etc...*/
und eine andere Datei: BaseFactory.h
#include "Base.h"
class BaseFactory
{
public:
BaseFactory(const string &sClassName){msClassName = sClassName;};
Base * Create()
{
if(msClassName == "DerivedA")
{
return new DerivedA();
}
else if(msClassName == "DerivedB")
{
return new DerivedB();
}
else if(/*etc...*/)
{
/*etc...*/
}
};
private:
string msClassName;
};
/*etc.*/
Gibt es eine Möglichkeit, diese Zeichenfolge irgendwie in einen tatsächlichen Typ (Klasse) zu konvertieren, sodass BaseFactory nicht alle möglichen abgeleiteten Klassen kennen muss und if () für jede von ihnen hat? Kann ich aus dieser Zeichenfolge eine Klasse erstellen?
Ich denke, dies kann in C # durch Reflection erfolgen. Gibt es etwas ähnliches in C ++?
c++
inheritance
factory
instantiation
Gal Goldman
quelle
quelle
Antworten:
Nein, es gibt keine, es sei denn, Sie führen das Mapping selbst durch. C ++ hat keinen Mechanismus zum Erstellen von Objekten, deren Typen zur Laufzeit festgelegt werden. Sie können diese Karte jedoch mit einer Karte selbst erstellen:
Und dann kannst du es tun
Eine neue Instanz erhalten. Eine andere Idee ist, die Typen selbst registrieren zu lassen:
Sie können ein Makro für die Registrierung erstellen
Ich bin mir sicher, dass es für diese beiden bessere Namen gibt. Eine andere Sache, die hier wahrscheinlich Sinn macht, ist
shared_ptr
.Wenn Sie eine Reihe nicht verwandter Typen haben, die keine gemeinsame Basisklasse haben, können Sie dem Funktionszeiger
boost::variant<A, B, C, D, ...>
stattdessen einen Rückgabetyp geben . Wenn Sie eine Klasse Foo, Bar und Baz haben, sieht es so aus:A
boost::variant
ist wie eine Gewerkschaft. Es erkennt, welcher Typ darin gespeichert ist, indem es nachschaut, welches Objekt zum Initialisieren oder Zuweisen verwendet wurde. Schauen Sie sich hier die Dokumentation an . Schließlich ist die Verwendung eines Rohfunktionszeigers auch etwas altmodisch. Moderner C ++ - Code sollte von bestimmten Funktionen / Typen entkoppelt werden. Vielleicht möchten Sie nachBoost.Function
einem besseren Weg suchen. Dann würde es so aussehen (die Karte):std::function
wird auch in der nächsten Version von C ++ verfügbar sein, einschließlichstd::shared_ptr
.quelle
DerivedB::reg
das tatsächlich initialisiert wird? Mein Verständnis ist, dass es möglicherweise überhaupt nicht konstruiert wird, wenn keine Funktion oder kein Objekt in der Übersetzungseinheitderivedb.cpp
gemäß 3.6.2 definiert ist.BaseFactory::map_type * BaseFactory::map = NULL;
in meiner CPP-Datei. Ohne dies beschwerte sich der Linker über unbekannte Symbolkarten.DerivedB::reg
wird musiphil nicht initialisiert, wenn keine seiner Funktionen oder Instanzen in der Übersetzungseinheit definiert istderivedb.cpp
. Das bedeutet, dass die Klasse erst registriert wird, wenn sie tatsächlich instanziiert wird. Kennt jemand eine Problemumgehung dafür?Nein, gibt es nicht. Meine bevorzugte Lösung für dieses Problem besteht darin, ein Wörterbuch zu erstellen, das den Namen der Erstellungsmethode zuordnet. Klassen, die so erstellt werden möchten, registrieren dann eine Erstellungsmethode im Wörterbuch. Dies wird im GoF-Musterbuch ausführlich erläutert .
quelle
Die kurze Antwort lautet: Sie können nicht. In diesen SO-Fragen erfahren Sie, warum:
quelle
Ich habe in einer anderen SO-Frage zu C ++ - Fabriken geantwortet. Bitte sehen Sie dort ob eine flexible Fabrik von Interesse ist. Ich versuche, einen alten Weg von ET ++ zu beschreiben, um Makros zu verwenden, der für mich großartig funktioniert hat.
ET ++ war ein Projekt, um alte MacApp auf C ++ und X11 zu portieren. In der Anstrengung begann Eric Gamma usw. über Design Patterns nachzudenken
quelle
boost :: function hat eine Factory-Vorlage, die sehr flexibel ist: http://www.boost.org/doc/libs/1_54_0/libs/functional/factory/doc/html/index.html
Ich bevorzuge es jedoch, Wrapper-Klassen zu generieren, die den Mechanismus für die Zuordnung und Objekterstellung verbergen. Das häufigste Szenario ist die Notwendigkeit, verschiedene abgeleitete Klassen einer Basisklasse Schlüsseln zuzuordnen, wobei für alle abgeleiteten Klassen eine gemeinsame Konstruktorsignatur verfügbar ist. Hier ist die Lösung, die ich bisher gefunden habe.
Ich bin im Allgemeinen gegen eine starke Verwendung von Makros, aber ich habe hier eine Ausnahme gemacht. Der obige Code generiert GENERIC_FACTORY_MAX_ARITY + 1-Versionen einer Klasse mit dem Namen GenericFactory_N für jedes N zwischen 0 und GENERIC_FACTORY_MAX_ARITY einschließlich.
Die Verwendung der generierten Klassenvorlagen ist einfach. Angenommen, Sie möchten, dass eine Factory von BaseClass abgeleitete Objekte mithilfe einer Zeichenfolgenzuordnung erstellt. Jedes der abgeleiteten Objekte verwendet 3 Ganzzahlen als Konstruktorparameter.
Der Destruktor der GenericFactory_N-Klasse ist virtuell, um Folgendes zu ermöglichen.
Beachten Sie, dass diese Zeile des generischen Factory-Generator-Makros
Angenommen, die generische Factory-Header-Datei heißt GenericFactory.hpp
quelle
Detaillösung zum Registrieren der Objekte und zum Zugreifen auf sie mit Zeichenfolgennamen.
common.h
::test1.h
::main.cpp
::Kompilieren und ausführen (Habe dies mit Eclipse gemacht)
Ausgabe:
quelle
Bedeutungsreflexion wie in Java. Hier gibt es einige Infos: http://msdn.microsoft.com/en-us/library/y0114hz2(VS.80).aspx
Im Allgemeinen suchen Sie Google nach "c ++ Reflection"
quelle
Tor Brede Vekterli bietet eine Boost-Erweiterung, die genau die Funktionalität bietet, die Sie suchen. Derzeit ist es mit den aktuellen Boost-Bibliotheken etwas umständlich, aber ich konnte es mit 1.48_0 zum Laufen bringen, nachdem ich seinen Basis-Namespace geändert hatte.
http://arcticinteractive.com/static/boost/libs/factory/doc/html/factory/factory.html#factory.factory.reference
Als Antwort auf diejenigen, die sich fragen, warum so etwas (als Reflexion) für c ++ nützlich ist - ich verwende es für Interaktionen zwischen der Benutzeroberfläche und einer Engine -, wählt der Benutzer eine Option in der Benutzeroberfläche aus, und die Engine verwendet die UI-Auswahlzeichenfolge. und erzeugt ein Objekt des gewünschten Typs.
Der Hauptvorteil der Verwendung des Frameworks hier (gegenüber der Verwaltung einer Obstliste irgendwo) besteht darin, dass die Registrierungsfunktion in der Definition jeder Klasse enthalten ist (und nur eine Codezeile benötigt, die die Registrierungsfunktion pro registrierter Klasse aufruft) - im Gegensatz zu einer Datei, die enthält die Fruchtliste, die bei jeder Ableitung einer neuen Klasse manuell hinzugefügt werden muss.
Ich habe die Fabrik zu einem statischen Mitglied meiner Basisklasse gemacht.
quelle
Dies ist das Werksmuster. Siehe Wikipedia (und dieses Beispiel). Sie können keinen Typ per se aus einer Zeichenfolge ohne einen ungeheuren Hack erstellen. Warum brauchst du das?
quelle