Frage

Ich habe eine Klasse, die einen Vektor einer anderen Klasse Objekte als Mitglied hat. In vielen Funktionen dieser Klasse habe ich gleiche Operation auf allen Objekten im Vektor zu tun:

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 vereinfachen und einen Weg finden, nicht den Vektor in jeder Funktion gehen andere zu kopieren.

Ich habe als eine Funktion erstellen, die einen Zeiger auf Funktion empfängt, und rufen die spitze Funktion auf jedem Mitglied eines Vektors. Aber ich bin nicht sicher, dass Zeiger auf Funktionen in C ++ ist eine gute Idee.

Ich habe auch über functors und functionoids , aber es wird mich zwingen, eine Klasse für jede Funktion zu erstellen, und es klingt wie ein Overkill.

Eine weitere mögliche Lösung ist die Schaffung eine Funktion, die eine Zeichenfolge empfängt, und ruft den Befehl entsprechend den string:

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 (nicht benötigte Schaffung eines Strings statt nur einen Funktionsaufruf) arbeitet, und schafft auch ein Problem, wenn Funktionen andere Signatur haben.

Also, was würden Sie empfehlen? Soll ich alles gleich lassen, wie es jetzt ist?

EDIT:. Ich kann nur STL verwenden und nicht steigern (alten Compiler)

War es hilfreich?

Lösung

Nun können Sie die neu zu schreiben für Schleifen Iteratoren und mehr von der STL wie folgt 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önnen Sie einige Makros verwenden, um zu vermeiden, dass eine Menge Abtippen, aber ich bin kein großer Fan davon. Ich persönlich mag die vielfältigen Funktionen über die einzelnen, die eine Befehlsfolge nimmt. Wie es gibt Ihnen mehr Flexibilität darüber, wie die Entscheidung getroffen wird.

Wenn Sie mit einer einzigen Funktion gehen Sie ein param nehmen zu entscheiden, welche zu tun, würde ich eine Enumeration und einen Schalter wie folgt verwenden, wäre es effizienter als Strings und eine Kaskadierung wenn. Auch in Ihrem Beispiel haben Sie das, wenn zu entscheiden, welche innerhalb der Schleife zu tun. Es ist effizienter, außerhalb der Schleife zu überprüfen und redundante Kopien der Schleife hat, da muss „welcher Befehl“ nur beschlossen pro Anruf einmal werden. (Hinweis: Sie können den Befehl ein Template-Parameter machen, wenn sie zum Zeitpunkt der Kompilierung bekannt, die es klingt wie es ist).

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 ist die & Klein :: was auch immer, was für ein Mitglied Funktionszeiger und nur passieren, dass als Parameter zu ersetzen. Sie können es sogar zu einer Vorlage machen.

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

Dann können Sie tun:

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

Die nette Sache über diesen und die regulären Parameter Methoden ist, dass Big muss nicht geändert werden, wenn Sie kleine ändern, um mehr Routinen zu haben! Ich denke, dies ist die bevorzugte Methode.

Wenn Sie in der Lage sein wollen, einen einzigen Parameter zu handhaben, müssen Sie einen bind2nd auch hinzuzufügen, hier ist ein komplettes 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);
}

Andere Tipps

Wenn Sie die Standardbibliothek verwenden, Sie einen Blick auf for_each .

Sie erwähnen, dass Funktionszeiger in C ++ nicht eine gute Idee sein könnte, aber - so dass Ihre Sorge ist, Geschwindigkeit - Sie müssen sehen, ob dieser Bereich auch ein Performance-Engpass ist sind Sie in, bevor sich Gedanken

Versuchen Sie boost :: function und < a href = "http://www.boost.org/doc/libs/1_37_0/libs/bind/bind.html" rel = "nofollow noreferrer"> 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));
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top