1052 Stimmen

Was sind C++-Funktoren und ihre Verwendung?

Ich höre immer wieder viel über Funktoren in C++. Kann mir jemand einen Überblick darüber geben, was sie sind und in welchen Fällen sie nützlich sein könnten?

130voto

Evgeny Lazin Punkte 8893

Kleine Ergänzung. Sie können verwenden boost::function , um Funktoren aus Funktionen und Methoden zu erstellen, etwa so:

class Foo
{
public:
    void operator () (int i) { printf("Foo %d", i); }
};
void Bar(int i) { printf("Bar %d", i); }
Foo foo;
boost::function<void (int)> f(foo);//wrap functor
f(1);//prints "Foo 1"
boost::function<void (int)> b(&Bar);//wrap normal function
b(1);//prints "Bar 1"

und Sie können boost::bind verwenden, um diesem Funktor einen Zustand hinzuzufügen

boost::function<void ()> f1 = boost::bind(foo, 2);
f1();//no more argument, function argument stored in f1
//and this print "Foo 2" (:
//and normal function
boost::function<void ()> b1 = boost::bind(&Bar, 2);
b1();// print "Bar 2"

und am nützlichsten, mit boost::bind und boost::function kann man einen Funktor aus einer Klassenmethode erstellen, eigentlich ist dies ein Delegat:

class SomeClass
{
    std::string state_;
public:
    SomeClass(const char* s) : state_(s) {}

    void method( std::string param )
    {
        std::cout << state_ << param << std::endl;
    }
};
SomeClass *inst = new SomeClass("Hi, i am ");
boost::function< void (std::string) > callback;
callback = boost::bind(&SomeClass::method, inst, _1);//create delegate
//_1 is a placeholder it holds plase for parameter
callback("useless");//prints "Hi, i am useless"

Sie können eine Liste oder einen Vektor von Funktoren erstellen

std::list< boost::function<void (EventArg e)> > events;
//add some events
....
//call them
std::for_each(
        events.begin(), events.end(), 
        boost::bind( boost::apply<void>(), _1, e));

Es gibt nur ein Problem mit all diesem Zeug, Compiler-Fehlermeldungen sind nicht für Menschen lesbar :)

128voto

James Curran Punkte 98228

Ein Functor ist ein Objekt, das sich wie eine Funktion verhält. Im Grunde genommen ist es eine Klasse, die definiert operator() .

class MyFunctor
{
   public:
     int operator()(int x) { return x * 2;}
}

MyFunctor doubler;
int x = doubler(5);

Der eigentliche Vorteil besteht darin, dass ein Funktor einen Zustand halten kann.

class Matcher
{
   int target;
   public:
     Matcher(int m) : target(m) {}
     bool operator()(int x) { return x == target;}
}

Matcher Is5(5);

if (Is5(n))    // same as if (n == 5)
{ ....}

66voto

mip Punkte 7905

Die Bezeichnung "Funktor" wurde traditionell verwendet in Kategorientheorie lange bevor C++ auf der Bildfläche erschien. Dies hat nichts mit dem C++-Konzept des Funktors zu tun. Es ist besser, den Namen Funktionsobjekt anstelle dessen, was wir in C++ "Funktor" nennen. In anderen Programmiersprachen werden ähnliche Konstrukte so genannt.

Wird anstelle der einfachen Funktion verwendet:

Merkmale:

  • Funktionsobjekt kann Zustand haben
  • Funktionsobjekt passt in OOP (es verhält sich wie jedes andere Objekt).

Contras:

  • Bringt mehr Komplexität in das Programm.

Wird anstelle von Funktionszeigern verwendet:

Merkmale:

  • Funktionsobjekte können oft inlined sein

Contras:

  • Funktionsobjekte können während der Laufzeit nicht mit anderen Funktionsobjekten ausgetauscht werden (es sei denn, sie erweitern eine Basisklasse, was einen gewissen Overhead bedeutet).

Wird anstelle der virtuellen Funktion verwendet:

Merkmale:

  • Funktionsobjekt (nicht-virtuell) erfordert keine vtable und kein Laufzeit-Dispatching, daher ist es in den meisten Fällen effizienter

Contras:

  • Funktionsobjekte können während der Laufzeit nicht mit anderen Funktionsobjekten ausgetauscht werden (es sei denn, sie erweitern eine Basisklasse, was einen gewissen Overhead bedeutet).

46voto

Johanne Irish Punkte 993

Für die Neulinge wie mich unter uns: Nach ein wenig Recherche habe ich herausgefunden, was der von Jalf gepostete Code bewirkt.

Ein Funktor ist ein Klassen- oder Strukturobjekt, das wie eine Funktion "aufgerufen" werden kann. Dies wird durch Überladen der Funktion () operator . Die () operator (ich bin mir nicht sicher, wie es heißt) kann eine beliebige Anzahl von Argumenten annehmen. Andere Operatoren benötigen nur zwei, z.B. der + operator kann nur zwei Werte annehmen (einen auf jeder Seite des Operators) und den Wert zurückgeben, für den Sie ihn überladen haben. Sie können eine beliebige Anzahl von Argumenten in eine () operator Das verleiht ihm seine Flexibilität.

Um einen Funktor zu erstellen, erstellen Sie zunächst Ihre Klasse. Dann erstellen Sie einen Konstruktor für die Klasse mit einem Parameter Ihrer Wahl von Typ und Name. Darauf folgt in derselben Anweisung eine Initialisierungsliste (die einen einzelnen Doppelpunkt-Operator verwendet, was mir ebenfalls neu war), die die Mitgliedsobjekte der Klasse mit dem zuvor deklarierten Parameter des Konstruktors konstruiert. Dann wird die () operator überlastet ist. Schließlich deklarieren Sie die privaten Objekte der Klasse oder Struktur, die Sie erstellt haben.

Mein Code (ich fand die Variablennamen von jalf verwirrend)

class myFunctor
{ 
    public:
        /* myFunctor is the constructor. parameterVar is the parameter passed to
           the constructor. : is the initializer list operator. myObject is the
           private member object of the myFunctor class. parameterVar is passed
           to the () operator which takes it and adds it to myObject in the
           overloaded () operator function. */
        myFunctor (int parameterVar) : myObject( parameterVar ) {}

        /* the "operator" word is a keyword which indicates this function is an 
           overloaded operator function. The () following this just tells the
           compiler that () is the operator being overloaded. Following that is
           the parameter for the overloaded operator. This parameter is actually
           the argument "parameterVar" passed by the constructor we just wrote.
           The last part of this statement is the overloaded operators body
           which adds the parameter passed to the member object. */
        int operator() (int myArgument) { return myObject + myArgument; }

    private: 
        int myObject; //Our private member object.
}; 

Wenn etwas davon ungenau oder einfach nur falsch ist, können Sie mich gerne korrigieren!

46voto

Matthew Crumley Punkte 98564

Wie bereits von anderen erwähnt, ist ein Funktor ein Objekt, das sich wie eine Funktion verhält, d.h. es überlädt den Funktionsaufrufoperator.

Funktoren werden häufig in STL-Algorithmen verwendet. Sie sind nützlich, weil sie den Zustand vor und zwischen Funktionsaufrufen festhalten können, wie ein Abschluss in funktionalen Sprachen. Sie könnten zum Beispiel einen MultiplyBy Funktor, der sein Argument mit einem bestimmten Betrag multipliziert:

class MultiplyBy {
private:
    int factor;

public:
    MultiplyBy(int x) : factor(x) {
    }

    int operator () (int other) const {
        return factor * other;
    }
};

Dann könnten Sie eine MultiplyBy Objekt mit einem Algorithmus wie std::transform:

int array[5] = {1, 2, 3, 4, 5};
std::transform(array, array + 5, array, MultiplyBy(3));
// Now, array is {3, 6, 9, 12, 15}

Ein weiterer Vorteil eines Funktors gegenüber einem Zeiger auf eine Funktion besteht darin, dass der Aufruf in mehr Fällen inlined werden kann. Wenn Sie einen Funktionszeiger an transform es sei denn que Aufruf eingefügt wurde und der Compiler weiß, dass Sie ihm immer dieselbe Funktion übergeben, kann er den Aufruf nicht über den Zeiger einfügen.

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