4 Stimmen

Iteration über einen Vektor und Aufruf von Funktionen

Ich habe eine Klasse, die einen Vektor einer anderen Klasse Objekte als Mitglied hat. In vielen Funktionen dieser Klasse muss ich dieselbe Operation für alle Objekte im Vektor durchführen:

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;
};

Ich möchte den Code zu vereinfachen, und einen Weg finden, nicht zu duplizieren gehen andere den Vektor in jeder Funktion.

Ich habe in Erwägung gezogen, eine Funktion zu erstellen, die einen Zeiger auf eine Funktion empfängt und die angezeigte Funktion für jedes Element eines Vektors aufruft. Aber ich bin mir nicht sicher, ob die Verwendung von Zeigern auf Funktionen in C++ eine gute Idee ist.

Ich habe mir auch Gedanken über Funktoren und Funktionsoide aber es wird mich zwingen, für jede Funktion eine Klasse zu erstellen, und das klingt nach einem Overkill.

Eine andere mögliche Lösung besteht darin, eine Funktion zu erstellen, die eine Zeichenkette empfängt und den Befehl entsprechend der Zeichenkette aufruft:

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");
}

Aber es könnte langsam arbeiten (unnötige Erstellung einer Zeichenkette anstelle eines Funktionsaufrufs), und schafft auch ein Problem, wenn Funktionen unterschiedliche Signatur haben.

Was würden Sie also empfehlen? Sollte ich alles so lassen, wie es jetzt ist?

EDIT: Ich kann nur STL und nicht Boost verwenden (alte Compiler).

16voto

Evan Teran Punkte 83711

Nun können Sie die for-Schleifen umschreiben, um Iteratoren und mehr von der STL wie diese zu verwenden:

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));
}

Darüber hinaus könnte man einige Makros verwenden, um das viele Abtippen zu vermeiden, aber ich bin kein großer Fan davon. Ich persönlich bevorzuge mehrere Funktionen gegenüber einer einzigen, die einen Befehlsstring benötigt. So hat man mehr Möglichkeiten, wie die Entscheidung getroffen wird.

Wenn Sie mit einer einzigen Funktion gehen, die einen Parameter zu entscheiden, welche zu tun, würde ich ein Enum und einen Schalter wie diese verwenden, wäre es effizienter als Zeichenfolgen und eine kaskadierende wenn. Außerdem haben Sie in Ihrem Beispiel das if, um zu entscheiden, was innerhalb der Schleife zu tun ist. Es ist effizienter, außerhalb der Schleife zu prüfen und redundante Kopien der Schleife zu haben, da "welcher Befehl" nur einmal pro Aufruf entschieden werden muss. (HINWEIS: Sie können den Befehl zu einem Template-Parameter machen, wenn er zur Kompilierzeit bekannt ist, was ja der Fall zu sein scheint).

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;
    }
};

Auch, wie Sie erwähnt, ist es ziemlich trivial zu ersetzen, die &Small::was auch immer, was ein Mitglied Funktion Zeiger und nur übergeben, dass als Parameter. Sie können sogar machen es eine Vorlage zu.

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

Dann können Sie das tun:

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

Das Schöne an dieser Methode und den normalen Parametermethoden ist, dass Big nicht geändert werden muss, wenn Sie Small ändern, um mehr Routinen zu haben! Ich denke, dies ist die bevorzugte Methode.

Wenn Sie nur einen einzigen Parameter verarbeiten wollen, müssen Sie auch ein bind2nd hinzufügen, hier ein vollständiges Beispiel:

#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);
}

4voto

Eddie Parker Punkte 4647

Wenn Sie die std-Bibliothek verwenden, sollten Sie einen Blick auf für_alle .

Sie erwähnen, dass die Verwendung von Funktionszeigern in C++ möglicherweise keine gute Idee ist, aber - wenn Ihre Sorge die Geschwindigkeit ist - müssen Sie sehen, ob dies überhaupt ein Leistungsengpass ist, in dem Sie sich befinden, bevor Sie sich Sorgen machen.

0voto

dalle Punkte 17489

Versuchen Sie boost::function y 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));
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X