2010-05-15 3 views
1

Je suis en train de créer un tableau de la structure qui relie les chaînes d'entrée aux classes comme suit:classe tableau de structure de recherche en C++

struct {string command; CommandPath cPath;} cPathLookup[] = { 
    {"set an alarm", AlarmCommandPath}, 
    {"send an email", EmailCommandPath}, 
    {"", NULL} 
}; 

qui sera utilisé comme suit:

CommandPath *cPath = NULL; 
string input; 
getline(cin, input); 
for(int i = 0; cPathLookup[i] != ""; i++) { 
     if(cPathLookup[i].command == input) 
       cPath = new cPathLookup[i].cPath; 
} 

De toute évidence, ce code n'a pas de sens, mais je pense que mon intention est apparente - en fonction de l'entrée, je voudrais que cPath soit initialisé comme un nouveau AlarmCommandPath ou un nouveau EmailCommandPath. Je pourrais le gérer avec une fonction retournant une instance en fonction de l'entrée, mais toute une séquence d'ifs semble juste inélégante.

Je devrais également noter que, dans le cas où ce n'est pas apparent et important, que AlarmCommandPath et EmailCommandPath sont dérivés de CommandPath, et CommandPath est une classe abstraite.

Merci pour toute aide que vous pouvez offrir.

EDIT: Je viens de remarquer que, malgré CommandPath être abstraite, j'ai une déclaration:

CommandPath *cPath = NULL; 

dans le code de travail. Pourquoi cela compile-t-il?

Répondre

1

AlarmCommandPath et EmailCommandPath sont dérivés de COmmandPath, correct?

Dans ce cas, vous ne pouvez pas affecter une instance de AlarmCommandPath/EmailCommandPath à CommandPath - c'est techniquement possible, mais cela ne fera pas ce que vous voulez. L'instance CommandPath restera une instance de CommandPath (il aura une table de fonction virtuelle de CommandPath), peu importe ce que vous lui attribuez.

Vous devez utiliser des méthodes d'usine (une fonction qui retournera CommandPath *). Quelque chose comme ça:

struct A{ 
}; 

struct B: public A{ 
}; 

struct C: public A{ 
}; 

A* factoryA(){ 
    return new A(); 
} 

A* factoryB(){ 
    return new B(); 
} 

A* factoryC(){ 
    return new C(); 
} 

typedef A* (*FactoryMethod)(); 

struct{ 
    const char* command; 
    FactoryMethod factoryMethod; 
} factoryTable[] = { 
    {"A", factoryA}, 
    {"B", factoryB}, 
    {"C", factoryC}, 
    {0,0} 
}; 
+0

cela peut-il être fait sans allocations de tas? Je veux dire, ces méthodes d'usine peuvent-elles construire un A ou B ou C sur la pile et le retourner en valeur? – Segfault

1

Vous ne pouvez pas stocker un type dans une structure, mais vous pouvez stocker un pointeur sur une fonction qui crée un type.

CommandPath * CreateEmail() { 
    return new EmailCommandPath; 
} 

CommandPath * CreateAlarm() { 
    return new AlarmCommandPath; 
} 

Ensuite, votre struct ressemble:

typedef Command * (* CreateFunc)(); 
struct MyMap { 
    string command; 
    CreateFunc func; 
}; 

et la carte:

MyMap m[] = {{"email", CreateEmail }, {"alarm", CreateAlarm}}; 

Vous regardez ensuite comme avant d'obtenir un indice i, et l'utiliser:

CommandPath * p = m[i].func(): 

Et vous pouvez créer des pointeurs pour t abstrait ypes - vous ne pouvez pas créer d'instances d'entre eux.

0

Je suppose que vous essayez de mettre en œuvre une recherche de table en remplacement d'une grande instruction if \ else dans votre système.

Pour plus de clarté, je choisirais plutôt un motif de conception d'usine. Avoir une grande logique if/else n'est vraiment mauvais que s'il est répété autour de votre code dans de nombreux endroits. Tant que c'est dans un endroit, c'est-à-dire une usine, à mon avis, vous avez un bon design.

0

Personnellement, je ne le vois pas comme étant un énorme problème si vous avez juste 1 usine pour créer différents "CommandPaths" pour différentes valeurs de la chaîne qu'il reçoit. Quoi qu'il en soit, votre code ne fonctionnera pas car vous ne pouvez pas stocker les types comme vous l'essayez. Si je devais faire ceci, alors pour un j'emploierais des pointeurs de fonction aux fonctions d'usine et utiliserais un std :: map pour mapper des chaînes à ceux-ci, comme montré dans ce code, et peut emballer les pointeurs dans un intelligent approprié -pointer, au lieu d'utiliser des pointeurs premières:

#include <string> 
#include <map> 

struct A { 
}; 

struct B : public A { 
}; 

struct C : public A { 
}; 

A *BFactory(){ 
    return new B(); 
} 

A *CFactory(){ 
    return new C(); 
} 

typedef A *(*Factory)(); 
typedef std::pair<std::string,Factory> Pair; 
typedef std::map<std::string,Factory> Map; 

Pair Pairs[] = 
{ 
    std::make_pair("alarm", BFactory), 
    std::make_pair("email", CFactory) 
}; 

Map Lookup(Pairs, Pairs + sizeof(Pairs)/sizeof(*Pairs)); 

A *CreateInstance(const std::string &Type) 
{ 
    Map::const_iterator i = Lookup.find(Type); 

    if(i != Lookup.end()) 
     return i->second(); 
    else 
     return 0; 
} 

Quant à votre question sur les pointeurs et les classes abstraites, vous pouvez avoir un pointeur vers une classe abstraite, mais vous ne pouvez pas instancier une classe abstraite.