2009-01-19 12 views
4

J'ai une classe qui a un vecteur d'un autre objet de classe en tant que membre. Dans de nombreuses fonctions de cette classe que je dois faire même opération sur tous les objets du vecteur:Itérer sur les fonctions vectorielles et appelantes

class Small 
{ 
    public: 
    void foo(); 
    void bar(int x); 
    // and many more functions 
}; 

class Big 
{ 
    public: 
    void foo() 
    { 
     for (size_t i = 0; i < VectorOfSmalls.size(); i++) 
      VectorOfSmalls[i]->foo(); 
    } 
    void bar(int x) 
    { 
     for (size_t i = 0; i < VectorOfSmalls.size(); i++) 
      VectorOfSmalls[i]->bar(x); 
    } 
    // and many more functions 
    private: 
    vector<Small*> VectorOfSmalls; 
}; 

Je veux simplifier le code, et trouver un moyen de ne pas dupliquer aller autre vecteur dans toutes les fonctions.

J'ai envisagé de créer une fonction qui reçoit un pointeur sur la fonction, et appelle la fonction pointée sur chaque membre d'un vecteur. Mais je ne suis pas sûr que l'utilisation de pointeurs vers des fonctions en C++ soit une bonne idée.

J'ai aussi pensé aux foncteurs et au functionoids, mais ça va me forcer à créer une classe pour chaque fonction et ça sonne comme une overkill.

Une autre solution possible est de créer une fonction qui reçoit une chaîne, et appelle la commande en fonction de la chaîne:

void Big::call_command(const string & command) 
{ 
    for (size_t i = 0; i < VectorOfSmalls.size(); i++) 
    { 
     if (command == "foo") 
      VectorOfSmalls[i]->foo(); 
     else if (command == "bar") 
      VectorOfSmalls[i]->bar(); 
    } 
} 
void Big::foo() 
{ 
    call_command("foo"); 
} 

Mais il pourrait travailler lent (création inutile d'une chaîne au lieu d'un appel de fonction) et crée également un problème si les fonctions ont une signature différente.

Alors, que recommanderiez-vous? Est-ce que je devrais laisser tout le même comme c'est maintenant?

EDIT: Je ne peux utiliser que STL et pas boost (anciens compilateurs).

Répondre

16

Eh bien, vous pouvez réécrire le pour les boucles à utiliser itérateurs et plus du TSL comme ceci:

void foo() { 
    std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(&Small::foo)); 
} 

void bar() { 
    std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(&Small::bar)); 
} 

au-delà, vous pouvez utiliser des macros pour éviter de retaper que beaucoup, mais je ne suis pas un grand fan de ça. Personnellement, j'aime les fonctions multiples sur le seul qui prend une chaîne de commande. Comme il vous donne plus de polyvalence sur la façon dont la décision est prise.

Si vous allez avec une seule fonction en prenant un param pour décider quoi faire, j'utiliserais une énumération et un commutateur comme celui-ci, ce serait plus efficace que les chaînes et une cascade si. De plus, dans votre exemple, vous avez le choix de décider quoi faire à l'intérieur de la boucle. Il est plus efficace de vérifier en dehors de la boucle et d'avoir des copies redondantes de la boucle puisque "quelle commande" ne doit être décidée qu'une fois par appel. (NOTE: vous pouvez faire de la commande un paramètre template s'il est connu au moment de la compilation, ce qui semble être le cas).

class Big { 
public: 
    enum Command { 
     DO_FOO, 
     DO_BAR 
    }; 

void doit(Command cmd) { 
    switch(cmd) { 
    case DO_FOO: 
     std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(&Small::foo)); 
     break; 
    case DO_BAR: 
     std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(&Small::bar)); 
     break; 
    } 
}; 

En outre, comme vous l'avez mentionné, il est assez trivial pour remplacer le petit & :: quelle que soit, quel pointeur de fonction membre et juste passer que comme paramètre. Vous pouvez même en faire un modèle aussi.

class Big { 
public: 
    template<void (Small::*fn)()> 
    void doit() { 
     std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(fn)); 
    } 
}; 

Ensuite, vous pouvez faire:

Big b; 
b.doit<&Small::foo>(); 
b.doit<&Small::bar>(); 

La bonne chose à la fois ceci et les méthodes de paramètres réguliers est que Big n'a pas besoin d'être modifié si vous changez petit pour avoir plus de routines! Je pense que c'est la méthode préférée.

Si vous voulez être en mesure de gérer un seul paramètre, vous aurez besoin d'ajouter un bind2nd aussi, voici un exemple complet:

#include <algorithm> 
#include <functional> 
#include <iostream> 
#include <vector> 

class Small { 
public: 
    void foo() { std::cout << "foo" << std::endl; } 
    void bar(int x) { std::cout << "bar" << std::endl; } 
}; 


class Big { 
public: 
    template<void (Small::*fn)()> 
    void doit() { 
     std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(fn)); 
    } 

    template<class T, void (Small::*fn)(T)> 
    void doit(T x) { 
     std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::bind2nd(std::mem_fun(fn), x)); 
    } 
public: 
    std::vector<Small *> VectorOfSmalls; 
}; 

int main() { 
    Big b; 
    b.VectorOfSmalls.push_back(new Small); 
    b.VectorOfSmalls.push_back(new Small); 

    b.doit<&Small::foo>(); 
    b.doit<int, &Small::bar>(5); 
} 
+0

Merci Evan. Mais std :: mem_fn fait partie de boost et pas de STL. Peut-être qu'il y a une autre façon d'utiliser for_each dans mon cas? Et qu'en est-il des fonctions avec des signatures différentes? –

+0

oops, typo: mem_fn fait partie de boost, cependant std :: mem_fun fait partie de la STL. –

+0

comme pour les fonctions avec des signatures différentes, vous pouvez probablement utiliser quelques fonctions de modèle pour couvrir toutes les possibilités. –

4

Si vous utilisez la bibliothèque std, vous devriez jeter un oeil à for_each. Vous mentionnez que l'utilisation de pointeurs de fonction en C++ n'est peut-être pas une bonne idée, mais - en vous souciant de la rapidité - vous devez voir s'il s'agit d'une zone de goulot d'étranglement, avant de vous inquiéter.

0

Essayez boost::function et boost::bind:

void Big::call_command(const boost::function<void (Small*)>& f) 
{ 
    for (size_t i = 0; i < VectorOfSmalls.size(); i++) 
    { 
     f(VectorOfSmalls[i]); 
    } 
} 

int main() 
{ 
    Big b; 
    b.call_command(boost::bind(&Small::foo, _1)); 
    b.call_command(boost::bind(&Small::bar, _1, 5)); 
} 
+0

une bonne solution, (bien que vous devriez utiliser std :: for_each). Mais malheureusement, il a dit doper une option viable. –

+0

Pourquoi la downvote? J'ai répondu avant le montage où il a ajouté qu'il ne pouvait pas utiliser boost. – dalle