J'ai un fichier: Base.h
class Base;
class DerivedA : public Base;
class DerivedB : public Base;
/*etc...*/
et un autre fichier: 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.*/
Existe-t-il un moyen de convertir en quelque sorte cette chaîne en un type réel (classe), de sorte que BaseFactory n'ait pas à connaître toutes les classes dérivées possibles et à avoir if () pour chacune d'elles? Puis-je produire une classe à partir de cette chaîne?
Je pense que cela peut être fait en C # via Reflection. Y a-t-il quelque chose de similaire en C ++?
c++
inheritance
factory
instantiation
Gal Goldman
la source
la source
Réponses:
Non, il n'y en a pas, à moins que vous ne fassiez la cartographie vous-même. C ++ n'a aucun mécanisme pour créer des objets dont les types sont déterminés au moment de l'exécution. Vous pouvez cependant utiliser une carte pour faire ce mappage vous-même:
Et puis tu peux faire
Obtenir une nouvelle instance. Une autre idée est de faire enregistrer les types eux-mêmes:
Vous pouvez décider de créer une macro pour l'enregistrement
Je suis sûr qu'il y a de meilleurs noms pour ces deux là. Une autre chose qui a probablement du sens à utiliser ici est
shared_ptr
.Si vous avez un ensemble de types non liés qui n'ont pas de classe de base commune, vous pouvez attribuer au pointeur de fonction un type de retour à la
boost::variant<A, B, C, D, ...>
place. Comme si vous avez une classe Foo, Bar et Baz, cela ressemble à ceci:A
boost::variant
est comme une union. Il sait quel type y est stocké en recherchant quel objet a été utilisé pour l'initialiser ou l'assigner. Jetez un œil à sa documentation ici . Enfin, l'utilisation d'un pointeur de fonction brute est également un peu ancienne. Le code C ++ moderne doit être découplé des fonctions / types spécifiques. Vous voudrez peut-être examinerBoost.Function
pour trouver une meilleure façon. Cela ressemblerait à ceci alors (la carte):std::function
sera également disponible dans la prochaine version de C ++, y comprisstd::shared_ptr
.la source
DerivedB::reg
est réellement initialisé? Je crois comprendre qu'il peut ne pas être construit du tout si aucune fonction ou aucun objet n'est défini dans l'unité de traductionderivedb.cpp
, conformément à 3.6.2.BaseFactory::map_type * BaseFactory::map = NULL;
dans mon fichier cpp. Sans cela, l'éditeur de liens s'est plaint de la carte des symboles inconnus.DerivedB::reg
n'est pas initialisé si aucune de ses fonctions ou instances n'est définie dans l'unité de traductionderivedb.cpp
. Cela signifie que la classe n'est pas enregistrée tant qu'elle n'est pas réellement instanciée. Quelqu'un connaît-il une solution de contournement pour cela?Non, il n'y en a pas. Ma solution préférée à ce problème est de créer un dictionnaire qui mappe le nom à la méthode de création. Les classes qui veulent être créées de cette manière enregistrent ensuite une méthode de création avec le dictionnaire. Ceci est discuté en détail dans le livre des modèles du GoF .
la source
La réponse courte est que vous ne pouvez pas. Consultez ces questions SO pour savoir pourquoi:
la source
J'ai répondu à une autre question SO sur les usines C ++. Veuillez y voir si une usine flexible présente un intérêt. J'essaie de décrire une ancienne méthode d'ET ++ pour utiliser des macros qui a très bien fonctionné pour moi.
ET ++ était un projet de portage de l'ancien MacApp vers C ++ et X11. Dans cet effort, Eric Gamma, etc. a commencé à penser aux modèles de conception
la source
boost :: Functional a un template d'usine assez flexible: http://www.boost.org/doc/libs/1_54_0/libs/functional/factory/doc/html/index.html
Ma préférence est cependant de générer des classes wrapper qui masquent le mécanisme de mappage et de création d'objets. Le scénario courant que je rencontre est la nécessité de mapper différentes classes dérivées d'une classe de base vers des clés, où les classes dérivées ont toutes une signature de constructeur commune disponible. Voici la solution que j'ai trouvée jusqu'à présent.
Je suis généralement opposé à une utilisation intensive des macros, mais j'ai fait une exception ici. Le code ci-dessus génère des versions GENERIC_FACTORY_MAX_ARITY + 1 d'une classe nommée GenericFactory_N, pour chaque N compris entre 0 et GENERIC_FACTORY_MAX_ARITY inclus.
L'utilisation des modèles de classe générés est facile. Supposons que vous souhaitiez qu'une fabrique crée des objets dérivés BaseClass à l'aide d'un mappage de chaîne. Chacun des objets dérivés prend 3 entiers comme paramètres de constructeur.
Le destructeur de classe GenericFactory_N est virtuel pour permettre ce qui suit.
Notez que cette ligne de la macro générique du générateur d'usine
Suppose que le fichier d'en-tête de fabrique générique est nommé GenericFactory.hpp
la source
Solution détaillée pour enregistrer les objets et y accéder avec des noms de chaîne.
common.h
:test1.h
:main.cpp
:Compilez et exécutez-le (l'avez fait avec Eclipse)
Production:
la source
Signification de la réflexion comme en Java. il y a quelques informations ici: http://msdn.microsoft.com/en-us/library/y0114hz2(VS.80).aspx
De manière générale, recherchez sur Google "réflexion C ++"
la source
Tor Brede Vekterli fournit une extension boost qui donne exactement la fonctionnalité que vous recherchez. Actuellement, il convient légèrement aux bibliothèques boost actuelles, mais j'ai pu le faire fonctionner avec 1.48_0 après avoir changé son espace de noms de base.
http://arcticinteractive.com/static/boost/libs/factory/doc/html/factory/factory.html#factory.factory.reference
En réponse à ceux qui se demandent pourquoi une telle chose (comme réflexion) serait utile pour c ++ - je l'utilise pour les interactions entre l'interface utilisateur et un moteur - l'utilisateur sélectionne une option dans l'interface utilisateur, et le moteur prend la chaîne de sélection de l'interface utilisateur, et produit un objet du type souhaité.
Le principal avantage de l'utilisation du cadre ici (par rapport au maintien d'une liste de fruits quelque part) est que la fonction d'enregistrement est dans la définition de chaque classe (et ne nécessite qu'une seule ligne de code appelant la fonction d'enregistrement par classe enregistrée) - par opposition à un fichier contenant la liste de fruits, qui doit être ajoutée manuellement à chaque fois qu'une nouvelle classe est dérivée.
J'ai fait de l'usine un membre statique de ma classe de base.
la source
C'est le modèle d'usine. Voir wikipedia (et cet exemple). Vous ne pouvez pas créer un type en soi à partir d'une chaîne sans un hack flagrant. Pourquoi en avez-vous besoin?
la source