Wann und wie verwendet man in C++ eine Callback-Funktion?
EDIT :
Ich würde gerne ein einfaches Beispiel für eine Callback-Funktion sehen.
Wann und wie verwendet man in C++ eine Callback-Funktion?
EDIT :
Ich würde gerne ein einfaches Beispiel für eine Callback-Funktion sehen.
In C++ gibt es kein explizites Konzept für eine Rückruf-Funktion. Callback-Mechanismen werden häufig über Funktionszeiger, Funktorobjekte oder Callback-Objekte implementiert. Die Programmierer müssen Callback-Funktionen explizit entwerfen und implementieren.
Aufgrund von Rückmeldungen bearbeiten:
Trotz des negativen Feedbacks, das diese Antwort erhalten hat, ist sie nicht falsch. Ich werde versuchen, besser zu erklären, worauf ich hinaus will.
C und C++ bieten alles, was Sie für die Implementierung von Callback-Funktionen benötigen. Die häufigste und trivialste Art, eine Callback-Funktion zu implementieren, besteht darin, einen Funktionszeiger als Funktionsargument zu übergeben.
Callback-Funktionen und Funktionszeiger sind jedoch nicht gleichbedeutend. Ein Funktionszeiger ist ein Sprachmechanismus, während eine Rückruffunktion ein semantisches Konzept ist. Funktionszeiger sind nicht die einzige Möglichkeit, eine Callback-Funktion zu implementieren - Sie können auch Funktoren und sogar virtuelle Funktionen verwenden. Was einen Funktionsaufruf zu einem Callback macht, ist nicht der Mechanismus, der zur Identifizierung und zum Aufruf der Funktion verwendet wird, sondern der Kontext und die Semantik des Aufrufs. Etwas als Callback-Funktion zu bezeichnen, impliziert eine größere als die normale Trennung zwischen der aufrufenden Funktion und der spezifischen Funktion, die aufgerufen wird, eine lockerere konzeptionelle Kopplung zwischen dem Aufrufer und dem Aufgerufenen, wobei der Aufrufer die explizite Kontrolle darüber hat, was aufgerufen wird. Es ist dieser unscharfe Begriff der lockeren konzeptionellen Kopplung und der aufrufergesteuerten Funktionsauswahl, der etwas zu einer Callback-Funktion macht, nicht die Verwendung eines Funktionszeigers.
Zum Beispiel, die .NET-Dokumentation für IFormatProvider sagt, dass "GetFormat ist eine Rückrufmethode" auch wenn es sich nur um eine gewöhnliche Schnittstellenmethode handelt. Ich glaube nicht, dass jemand behaupten würde, dass alle virtuellen Methodenaufrufe Callback-Funktionen sind. Was GetFormat zu einer Callback-Methode macht, ist nicht die Mechanik der Übergabe oder des Aufrufs, sondern die Semantik des Aufrufers, der auswählt, welche GetFormat-Methode des Objekts aufgerufen werden soll.
Einige Sprachen enthalten Funktionen mit expliziter Callback-Semantik, typischerweise im Zusammenhang mit Ereignissen und Ereignisbehandlung. Zum Beispiel, C# hat die Veranstaltung Typ, dessen Syntax und Semantik ausdrücklich auf das Konzept der Rückrufe ausgerichtet ist. Visual Basic hat seine Griffe Klausel, die eine Methode explizit als Callback-Funktion deklariert und dabei das Konzept von Delegaten oder Funktionszeigern abstrahiert. In diesen Fällen ist das semantische Konzept eines Rückrufs in die Sprache selbst integriert.
C und C++ hingegen betten die semantisches Konzept von Callback-Funktionen fast genauso explizit. Die Mechanismen sind vorhanden, die integrierte Semantik ist es nicht. Man kann Callback-Funktionen sehr gut implementieren, aber um etwas Anspruchsvolleres zu erhalten, das explizite Callback-Semantik beinhaltet, muss man es auf dem aufbauen, was C++ bietet, wie z.B. Qt mit seinem Signale und Slots .
Kurz gesagt, C++ hat alles, was man braucht, um Rückrufe zu implementieren, oft ganz einfach und trivial mit Funktionszeigern. Was es nicht hat, sind Schlüsselwörter und Funktionen, deren Semantik spezifisch für Rückrufe ist, wie z.B. erhöhen , ausstrahlen. , Griffe , Ereignis += , usw. Wenn Sie von einer Sprache mit diesen Arten von Elementen kommen, wird sich die native Callback-Unterstützung in C++ kastriert fühlen.
Callback-Funktionen sind Teil des C-Standards, und damit auch Teil von C++. Aber wenn Sie mit C++ arbeiten, würde ich vorschlagen, dass Sie die Beobachtungsmuster stattdessen: http://en.wikipedia.org/wiki/Observer_pattern
Siehe die obige Definition, in der es heißt, dass eine Callback-Funktion an eine andere Funktion weitergegeben und irgendwann aufgerufen wird.
In C++ ist es wünschenswert, dass Callback-Funktionen eine Klassenmethode aufrufen. Wenn Sie dies tun, haben Sie Zugriff auf die Mitgliedsdaten. Wenn Sie die C-Methode zur Definition eines Rückrufs verwenden, müssen Sie ihn auf eine statische Mitgliedsfunktion verweisen. Dies ist nicht sehr wünschenswert.
Hier sehen Sie, wie Sie Rückrufe in C++ verwenden können. Nehmen Sie 4 Dateien an. Ein Paar von .CPP/.H-Dateien für jede Klasse. Klasse C1 ist die Klasse mit einer Methode, die wir zurückrufen wollen. C2 ruft die Methode von C1 zurück. In diesem Beispiel benötigt die Callback-Funktion 1 Parameter, den ich dem Leser zuliebe hinzugefügt habe. Das Beispiel zeigt keine Objekte, die instanziiert und verwendet werden. Ein Anwendungsfall für diese Implementierung ist, wenn Sie eine Klasse haben, die Daten liest und in einem temporären Bereich speichert, und eine andere, die die Daten weiterverarbeitet. Mit einer Callback-Funktion kann der Callback dann für jede gelesene Datenzeile diese verarbeiten. Durch diese Technik wird der Overhead durch den erforderlichen temporären Speicherplatz reduziert. Sie ist besonders nützlich für SQL-Abfragen, die eine große Menge an Daten zurückgeben, die dann nachbearbeitet werden müssen.
/////////////////////////////////////////////////////////////////////
// C1 H file
class C1
{
public:
C1() {};
~C1() {};
void CALLBACK F1(int i);
};
/////////////////////////////////////////////////////////////////////
// C1 CPP file
void CALLBACK C1::F1(int i)
{
// Do stuff with C1, its methods and data, and even do stuff with the passed in parameter
}
/////////////////////////////////////////////////////////////////////
// C2 H File
class C1; // Forward declaration
class C2
{
typedef void (CALLBACK C1::* pfnCallBack)(int i);
public:
C2() {};
~C2() {};
void Fn(C1 * pThat,pfnCallBack pFn);
};
/////////////////////////////////////////////////////////////////////
// C2 CPP File
void C2::Fn(C1 * pThat,pfnCallBack pFn)
{
// Call a non-static method in C1
int i = 1;
(pThat->*pFn)(i);
}
Die akzeptierte Antwort ist umfassend, aber im Zusammenhang mit der Frage möchte ich hier nur ein einfaches Beispiel anführen. Ich hatte einen Code, den ich vor langer Zeit geschrieben hatte. Ich wollte einen Baum in der Reihenfolge durchlaufen (linker Knoten, dann Wurzelknoten, dann rechter Knoten) und immer, wenn ich einen Knoten erreiche, wollte ich in der Lage sein, eine beliebige Funktion aufzurufen, damit sie alles tun kann.
void inorder_traversal(Node *p, void *out, void (*callback)(Node *in, void *out))
{
if (p == NULL)
return;
inorder_traversal(p->left, out, callback);
callback(p, out); // call callback function like this.
inorder_traversal(p->right, out, callback);
}
// Function like bellow can be used in callback of inorder_traversal.
void foo(Node *t, void *out = NULL)
{
// You can just leave the out variable and working with specific node of tree. like bellow.
// cout << t->item;
// Or
// You can assign value to out variable like below
// Mention that the type of out is void * so that you must firstly cast it to your proper out.
*((int *)out) += 1;
}
// This function use inorder_travesal function to count the number of nodes existing in the tree.
void number_nodes(Node *t)
{
int sum = 0;
inorder_traversal(t, &sum, foo);
cout << sum;
}
int main()
{
Node *root = NULL;
// What These functions perform is inserting an integer into a Tree data-structure.
root = insert_tree(root, 6);
root = insert_tree(root, 3);
root = insert_tree(root, 8);
root = insert_tree(root, 7);
root = insert_tree(root, 9);
root = insert_tree(root, 10);
number_nodes(root);
}
Boosts Signale2 ermöglicht es Ihnen, generische Mitgliedsfunktionen zu abonnieren (ohne Vorlagen!), und zwar auf eine thread-sichere Weise.
Beispiel: Document-View-Signale können verwendet werden, um eine flexible Dokument-Ansicht-Architekturen. Das Dokument wird ein Signal enthalten mit dem sich jede der Ansichten verbinden kann. Die folgende Dokumentenklasse definiert ein einfaches Textdokument, das mehrere Ansichten unterstützt. Beachten Sie, dass sie ein einziges Signal speichert, mit dem alle Ansichten verbunden werden.
class Document
{
public:
typedef boost::signals2::signal<void ()> signal_t;
public:
Document()
{}
/* Connect a slot to the signal which will be emitted whenever
text is appended to the document. */
boost::signals2::connection connect(const signal_t::slot_type &subscriber)
{
return m_sig.connect(subscriber);
}
void append(const char* s)
{
m_text += s;
m_sig();
}
const std::string& getText() const
{
return m_text;
}
private:
signal_t m_sig;
std::string m_text;
};
Als nächstes können wir damit beginnen, Ansichten zu definieren. Die folgende TextView-Klasse bietet eine einfache Ansicht des Dokumenttextes.
class TextView
{
public:
TextView(Document& doc): m_document(doc)
{
m_connection = m_document.connect(boost::bind(&TextView::refresh, this));
}
~TextView()
{
m_connection.disconnect();
}
void refresh() const
{
std::cout << "TextView: " << m_document.getText() << std::endl;
}
private:
Document& m_document;
boost::signals2::connection m_connection;
};
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.