web-dev-qa-db-fra.com

Qu'est-ce qu'un délégué C ++?

Quelle est l'idée générale d'un délégué en C++? Que sont-ils, comment sont-ils utilisés et à quoi servent-ils?

J'aimerais tout d'abord en apprendre davantage à leur sujet, mais il serait également utile de disposer de quelques informations sur les entrailles de ces choses.

Ce n'est pas du C++ à l'état pur ou le plus propre, mais je remarque que la base de code sur laquelle je travaille les a en abondance. J'espère les comprendre suffisamment pour que je puisse simplement les utiliser sans avoir à fouiller dans l'horrible horrible modèle imbriqué.

Ces deux The Code Project articles expliquent ce que je veux dire mais ne sont pas particulièrement succincts:

133
SirYakalot

Vous avez un nombre incroyable de choix pour atteindre les délégués en C++. Voici ceux qui me sont venus à l'esprit.


Option 1: foncteurs:

Un objet de fonction peut être créé en implémentant 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);

Option 2: expressions lambda ( C++ 11 seulement)

// Syntax is roughly: [capture](parameter list) -> return type {block}
// Some shortcuts exist
auto func = [](int i) -> double { return 2*i/1.15; };
double d = func(1);

Option 3: pointeurs de fonction

int f(double d) { ... }
typedef int (*MyFuncT) (double d);
MyFuncT fp = &f;
int a = fp(3.14);

Option 4: pointeur sur les fonctions membres (solution la plus rapide)

Voir délégué C++ rapide (sur le projet de code ).

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

Option 5: std :: function

(ou boost::function si votre bibliothèque standard ne le supporte pas). C'est plus lent, mais c'est le plus flexible.

#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

Option 6: liaison (en utilisant std :: bind )

Permet de définir certains paramètres à l’avance, pratique pour appeler une fonction membre par exemple.

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: modèles

Acceptez n'importe quoi tant qu'il correspond à la liste d'arguments.

template <class FunctionT>
int DoSomething(FunctionT func)
{
    return func(3.14);
}
159
J.N.

Un délégué est une classe qui englobe un pointeur ou une référence dans une instance d'objet, une méthode membre de la classe de cet objet à appeler sur cette instance d'objet et fournit une méthode pour déclencher cet appel.

Voici un exemple:

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 );
}
34
Grimm The Opiner

Le besoin d'implémentation de délégué C++ est un embarras de longue durée pour la communauté C++. Tous les programmeurs C++ aimeraient les avoir. Ils finissent donc par les utiliser malgré les faits qui:

  1. std::function() utilise des opérations de tas (et est hors de portée pour une programmation embarquée sérieuse).

  2. Toutes les autres implémentations font des concessions sur la portabilité ou la conformité standard à des degrés plus ou moins importants (veuillez vérifier en inspectant les différentes implémentations des délégués ici et sur codeproject). Je n'ai pas encore vu d'implémentation qui n'utilise pas de reinterpret_casts sauvage, des "prototypes" de classe imbriquée qui produisent, espérons-le, des pointeurs de fonction de la même taille que celle transmise par l'utilisateur, des astuces du compilateur telles que la première déclaration, puis la typedef, puis la nouvelle déclaration, cette fois, héritant d'une autre classe ou de techniques douteuses similaires. Bien que ce soit un grand accomplissement pour les développeurs qui ont construit cela, cela reste un triste témoignage sur l'évolution du C++.

  3. Il est rare de noter que plus de 3 révisions standard du langage C++ ont maintenant été prises en compte par les délégués. (Ou le manque de fonctionnalités linguistiques permettant une implémentation directe des délégués.)

  4. Avec la manière dont les fonctions lambda C++ 11 sont définies par le standard (chaque lambda a un type différent anonyme), la situation ne s’est améliorée que dans certains cas d’utilisation. Mais dans le cas d'utilisation de délégués dans des API de bibliothèque (DLL), les lambdas seuls ne sont toujours pas utilisables. La technique courante ici consiste à compresser le lambda dans un std :: function, puis à le transmettre à travers l'API.

16
BitTickler

Très simplement, un délégué fournit des fonctionnalités sur la façon dont un pointeur de fonction DEVRAIT fonctionner. Il existe de nombreuses limitations des pointeurs de fonction en C++. Un délégué utilise un modèle de modèle en coulisse pour créer un objet type-classe-fonction-type-pointeur qui fonctionne comme vous le souhaitez.

c'est-à-dire que vous pouvez les configurer pour pointer vers une fonction donnée et vous pouvez les faire circuler et les appeler quand et où vous le souhaitez.

Il y a de très bons exemples ici:

6
SirYakalot

Une option pour les délégués en C++ qui n'est pas autrement mentionnée ici est de le faire en C utilisant une fonction ptr et un argument contextuel. C'est probablement le même schéma que beaucoup de personnes qui posent cette question tentent d'éviter. Cependant, le modèle est portable, efficace et utilisable dans le code incorporé et dans le noyau.

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);
    ...
3
Joe

Équivalent Windows Runtime d'un objet fonction en C++ standard. On peut utiliser la fonction entière comme paramètre (en fait, c'est un pointeur de fonction). Il est principalement utilisé en conjonction avec des événements. Le délégué représente un contrat que les gestionnaires d'événements remplissent beaucoup. Cela facilite l'utilisation d'un pointeur de fonction.

1
nmserve