Was ist die allgemeine Idee eines Delegaten in C++? Was sind sie, wie werden sie verwendet und wofür werden sie verwendet?
Ich würde sie gerne zuerst auf „Black Box“-Weise kennenlernen, aber ein paar Informationen über die Eingeweide dieser Dinge wären auch großartig.
Dies ist nicht C++ in seiner reinsten oder saubersten Form, aber ich stelle fest, dass die Codebasis, an der ich arbeite, sie in Hülle und Fülle enthält. Ich hoffe, sie genug zu verstehen, damit ich sie einfach verwenden kann und mich nicht in die schreckliche Schrecklichkeit verschachtelter Vorlagen vertiefen muss.
Diese zwei Das Code-Projekt Artikel erklären, was ich meine, aber nicht besonders prägnant:
delegate ist kein gebräuchlicher Name im C++-Sprachgebrauch. Sie sollten der Frage einige Informationen hinzufügen, um den Kontext einzubeziehen, in dem Sie sie gelesen haben. Beachten Sie, dass das Muster zwar üblich sein kann, die Antworten jedoch unterschiedlich sein können, wenn Sie darüber sprechen delegieren im Allgemeinen oder im Kontext von C++CLI oder einer anderen Bibliothek, die eine bestimmte Implementierung von hat delegieren.
– David Rodríguez – Dribeas
5. März 2012 um 15:00 Uhr
2 jahre warten ?;)
– Rang 1
18. Juni 2013 um 14:39 Uhr
Warte noch…
– Grimm der Opiner
25. Juni 2014 um 7:56 Uhr
JN
Sie haben eine unglaubliche Anzahl von Möglichkeiten, um Delegates in C++ zu erreichen. Hier sind die, die mir in den Sinn gekommen sind.
Option 1: Funktoren:
Ein Funktionsobjekt kann durch Implementieren erstellt werden operator()
struct Functor
{
// Normal class/struct members
int operator()(double d) // Arbitrary return types and parameter list
{
return (int) d + 1;
}
};
// Use:
Functor f;
int i = f(3.14);
struct DelegateList
{
int f1(double d) { }
int f2(double d) { }
};
typedef int (DelegateList::* DelegateType)(double d);
DelegateType d = &DelegateList::f1;
DelegateList list;
int a = (list.*d)(3.14);
(oder boost::function wenn Ihre Standardbibliothek dies nicht unterstützt). Es ist langsamer, aber am flexibelsten.
#include <functional>
std::function<int(double)> f = [can be set to about anything in this answer]
// Usually more useful as a parameter to another functions
Ermöglicht das Einstellen einiger Parameter im Voraus, z. B. zum bequemen Aufrufen einer Member-Funktion.
struct MyClass
{
int DoStuff(double d); // actually a DoStuff(MyClass* this, double d)
};
std::function<int(double d)> f = std::bind(&MyClass::DoStuff, this, std::placeholders::_1);
// auto f = std::bind(...); in C++11
Option 7: Vorlagen
Akzeptiere alles, solange es mit der Argumentliste übereinstimmt.
template <class FunctionT>
int DoSomething(FunctionT func)
{
return func(3.14);
}
Tolle Liste, +1. Allerdings zählen hier nur zwei wirklich als Delegaten – das Erfassen von Lambdas und das zurückgegebene Objekt std::bindund beide tun wirklich dasselbe, außer dass Lambdas nicht in dem Sinne polymorph sind, dass sie unterschiedliche Argumenttypen akzeptieren können.
– Xeo
5. März 2012 um 14:46 Uhr
@JN: Warum empfehlen Sie, keine Funktionszeiger zu verwenden, scheinen aber mit der Verwendung der Member-Methodenzeiger einverstanden zu sein? Sie sind einfach identisch!
– Matthias M.
5. März 2012 um 14:46 Uhr
@MatthieuM. : gutes Argument. Ich hielt Funktionszeiger für veraltet, aber das ist wahrscheinlich nur mein persönlicher Geschmack.
– JN
5. März 2012 um 14:51 Uhr
@SirYakalot Etwas, das sich wie eine Funktion verhält, aber gleichzeitig den Zustand halten und wie jede andere Variable manipuliert werden kann. Eine Verwendung besteht beispielsweise darin, eine Funktion zu nehmen, die zwei Parameter benötigt, und den 1. Parameter zu zwingen, einen bestimmten Wert zu haben, um eine neue Funktion mit einem einzigen Parameter zu erstellen (binding in meiner Liste). Sie können dies nicht mit Funktionszeigern erreichen.
– JN
6. März 2012 um 15:05 Uhr
Sie existieren in C++ per se nicht. Daher die Liste.
– JN
25. Juli 2016 um 3:29 Uhr
Grimm Der Opiner
Ein Delegat ist eine Klasse, die einen Zeiger oder eine Referenz auf eine Objektinstanz umschließt, eine Mitgliedsmethode der Klasse dieses Objekts, die für diese Objektinstanz aufgerufen werden soll, und eine Methode bereitstellt, um diesen Aufruf auszulösen.
Hier ist ein Beispiel:
template <class T>
class CCallback
{
public:
typedef void (T::*fn)( int anArg );
CCallback(T& trg, fn op)
: m_rTarget(trg)
, m_Operation(op)
{
}
void Execute( int in )
{
(m_rTarget.*m_Operation)( in );
}
private:
CCallback();
CCallback( const CCallback& );
T& m_rTarget;
fn m_Operation;
};
class A
{
public:
virtual void Fn( int i )
{
}
};
int main( int /*argc*/, char * /*argv*/ )
{
A a;
CCallback<A> cbk( a, &A::Fn );
cbk.Execute( 3 );
}
was bietet eine Methode, um den Anruf auszulösen? der Delegierte? wie? Funktionszeiger?
– Dollarschein
6. März 2012 um 11:04 Uhr
Die Delegate-Klasse bietet eine Methode wie Execute() die den Funktionsaufruf für das Objekt auslösen, das der Delegat umschließt.
– Grimm der Opiner
6. März 2012 um 11:15 Uhr
Anstelle von Execute würde ich in Ihrem Fall dringend empfehlen, den Aufrufoperator zu überschreiben – void CCallback::operator()(int). Der Grund dafür liegt in der generischen Programmierung, da erwartet wird, dass aufrufbare Objekte wie eine Funktion aufgerufen werden. o.Execute(5) wäre inkompatibel, aber o(5) würde gut als aufrufbares Template-Argument passen. Diese Klasse könnte auch verallgemeinert werden, aber ich nehme an, Sie haben sie der Kürze halber einfach gehalten (was gut ist). Abgesehen von diesem Nitpick ist dies eine sehr nützliche Klasse.
– David Peterson
10. September 2014 um 15:19 Uhr
BitTickler
Die Notwendigkeit von C++-Delegatimplementierungen ist eine lang anhaltende Verlegenheit für die C++-Community. Jeder C++-Programmierer würde sie gerne haben, also verwenden sie sie schließlich trotz der Tatsachen, dass:
std::function() verwendet Heap-Operationen (und ist für ernsthafte eingebettete Programmierung unerreichbar).
Alle anderen Implementierungen machen Zugeständnisse in Richtung Portabilität oder Standardkonformität in größerem oder geringerem Maße (bitte überprüfen Sie dies, indem Sie die verschiedenen Delegiertenimplementierungen hier und auf codeproject überprüfen). Ich habe noch keine Implementierung gesehen, die keine wilden reinterpret_casts verwendet, verschachtelte Klassen “Prototypen”, die hoffentlich Funktionszeiger der gleichen Größe wie die vom Benutzer übergebenen erzeugen, Compiler-Tricks wie zuerst vorwärts deklarieren, dann typedef und dann erneut deklarieren, diesmal von einer anderen Klasse oder ähnlichen zwielichtigen Techniken erben. Obwohl es eine großartige Leistung für die Implementierer ist, die das gebaut haben, ist es immer noch ein trauriges Zeugnis dafür, wie sich C++ entwickelt.
Nur selten wird darauf hingewiesen, dass seit über 3 C++-Standardrevisionen Delegaten nicht richtig angesprochen wurden. (Oder das Fehlen von Sprachfeatures, die eine unkomplizierte Delegiertenimplementierung ermöglichen.)
Mit der Art und Weise, wie C++11-Lambda-Funktionen vom Standard definiert werden (jedes Lambda hat einen anonymen, anderen Typ), hat sich die Situation nur in einigen Anwendungsfällen verbessert. Aber für den Anwendungsfall der Verwendung von Delegaten in (DLL-)Bibliotheks-APIs, Lambdas allein sind immer noch nicht nutzbar. Die übliche Technik hier besteht darin, das Lambda zuerst in eine std::function zu packen und es dann über die API zu übergeben.
Ich habe eine Version von Elbert Mais generischen Callbacks in C++11 erstellt, die auf anderen Ideen basiert, die an anderer Stelle zu sehen sind, und es scheint, dass die meisten Probleme, die Sie in 2) anführen, geklärt sind. Das einzige verbleibende nörgelnde Problem für mich ist, dass ich mit einem Makro im Client-Code feststecke, um die Delegaten zu erstellen. Es gibt wahrscheinlich einen magischen Ausweg aus der Vorlage, den ich noch nicht gelöst habe.
– kert
25. August 2016 um 16:11 Uhr
Ich verwende std::function ohne Heap im eingebetteten System und es funktioniert immer noch.
– prasad
23. Oktober 2017 um 18:28 Uhr
std::function nicht stets dynamisch zuordnen
– Leichtigkeitsrennen im Orbit
13. Juli 2019 um 14:56 Uhr
Diese Antwort ist eher ein Scherz als eine tatsächliche Antwort
– Leichtigkeitsrennen im Orbit
13. Juli 2019 um 14:57 Uhr
Dollarscheibe
Ganz einfach, ein Delegat bietet Funktionen dafür, wie ein Funktionszeiger funktionieren SOLLTE. Es gibt viele Einschränkungen für Funktionszeiger in C++. Ein Delegat verwendet hinter den Kulissen ein bösartiges Template, um ein funktionszeigerartiges Ding der Template-Klasse zu erstellen, das so funktioniert, wie Sie es vielleicht möchten.
dh – Sie können sie so einstellen, dass sie auf eine bestimmte Funktion zeigen, und Sie können sie herumreichen und anrufen, wann und wo immer Sie möchten.
Eine Option für Delegaten in C++, die hier nicht anderweitig erwähnt wird, besteht darin, dies im C-Stil mit einer Funktion ptr und einem Kontextargument zu tun. Dies ist wahrscheinlich das gleiche Muster, das viele, die diese Frage stellen, zu vermeiden versuchen. Das Muster ist jedoch portabel, effizient und kann in eingebettetem und Kernel-Code verwendet werden.
class SomeClass
{
in someMember;
int SomeFunc( int);
static void EventFunc( void* this__, int a, int b, int c)
{
SomeClass* this_ = static_cast< SomeClass*>( this__);
this_->SomeFunc( a );
this_->someMember = b + c;
}
};
void ScheduleEvent( void (*delegateFunc)( void*, int, int, int), void* delegateContext);
...
SomeClass* someObject = new SomeObject();
...
ScheduleEvent( SomeClass::EventFunc, someObject);
...
nmserve
Windows-Runtime-Äquivalent eines Funktionsobjekts in Standard-C++. Man kann die ganze Funktion als Parameter verwenden (eigentlich ist das ein Funktionszeiger). Es wird hauptsächlich in Verbindung mit Veranstaltungen verwendet. Der Delegat stellt einen Vertrag dar, den Ereignishandler weitgehend erfüllen. Es erleichtert, wie ein Funktionszeiger arbeiten kann.
Reden Sie von Managed C++ unter .NET?
– Sergej Kalinitschenko
5. März 2012 um 14:20 Uhr
Hast du dir die angeschaut Wikipedia-Seite für Delegation?
– Matthijs Biermann
5. März 2012 um 14:20 Uhr
delegate
ist kein gebräuchlicher Name im C++-Sprachgebrauch. Sie sollten der Frage einige Informationen hinzufügen, um den Kontext einzubeziehen, in dem Sie sie gelesen haben. Beachten Sie, dass das Muster zwar üblich sein kann, die Antworten jedoch unterschiedlich sein können, wenn Sie darüber sprechen delegieren im Allgemeinen oder im Kontext von C++CLI oder einer anderen Bibliothek, die eine bestimmte Implementierung von hat delegieren.– David Rodríguez – Dribeas
5. März 2012 um 15:00 Uhr
2 jahre warten ?;)
– Rang 1
18. Juni 2013 um 14:39 Uhr
Warte noch…
– Grimm der Opiner
25. Juni 2014 um 7:56 Uhr