172 votes

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 d'abord apprendre à les connaître sous l'angle de la "boîte noire", mais un peu d'information sur les entrailles de ces choses serait également très utile.

Ce n'est pas le C++ dans sa forme la plus pure ou la plus propre, mais j'ai remarqué que la base de code où je travaille en possède en abondance. J'espère les comprendre suffisamment pour pouvoir les utiliser sans avoir à me plonger dans l'horreur des modèles imbriqués.

Ces deux Le projet Code articles expliquent ce que je veux dire, mais pas de manière particulièrement succincte :

4 votes

Parlez-vous de C++ géré sous .NET ?

2 votes

Avez-vous regardé le Page Wikipedia pour Délégation ?

6 votes

delegate n'est pas un nom courant dans le langage c++. Vous devez ajouter quelques informations à la question pour inclure le contexte dans lequel vous l'avez lu. Notez que même si le motif est commun, les réponses peuvent être différentes si vous parlez de délégué en général, ou dans le contexte de C++CLI ou de toute autre bibliothèque qui a une implémentation particulière de délégué .

196voto

J.N. Points 4858

Vous avez un nombre incroyable de choix pour obtenir des délégués en C++. Voici celles qui me sont venues à l'esprit.


Option 1 : foncteurs :

Un objet 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 vers les fonctions membres (solution la plus rapide)

Voir Délégué C++ rapide (sur Le projet 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::fonction

(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

Accepter n'importe quoi tant que cela correspond à la liste d'arguments.

template <class FunctionT>
int DoSomething(FunctionT func)
{
    return func(3.14);
}

2 votes

Excellente liste, +1. Cependant, seuls deux d'entre eux comptent vraiment comme des délégués ici - la capture des lambdas et l'objet retourné par la fonction std::bind Les deux font vraiment la même chose, sauf que les lambdas ne sont pas polymorphes dans le sens où ils peuvent accepter différents types d'arguments.

1 votes

@J.N : Pourquoi recommandez-vous de ne pas utiliser les pointeurs de fonctions mais semblez être d'accord avec l'utilisation des pointeurs de méthodes de membres ? Ils sont tout simplement identiques !

1 votes

@MatthieuM. : bon point. Je considérais les pointeurs de fonction comme un héritage, mais ce n'est probablement que mon goût personnel.

40voto

Grimm The Opiner Points 1203

Un délégué est une classe qui enveloppe un pointeur ou une référence à 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 );
}

0 votes

Qui fournit une méthode pour déclencher l'appel ? le délégué ? comment ? pointeur de fonction ?

0 votes

La classe de délégué offrira une méthode comme Execute() qui déclenchent l'appel de fonction sur l'objet que le délégué enveloppe.

4 votes

Au lieu d'Execute, je vous recommande vivement, dans votre cas, de surcharger l'opérateur d'appel - void CCallback::operator()(int). La raison en est qu'en programmation générique, les objets appelables sont censés être appelés comme une fonction. o.Execute(5) serait incompatible, mais o(5) conviendrait parfaitement comme argument de modèle appelable. Cette classe pourrait également être généralisée, mais je suppose que vous l'avez gardée simple pour la brièveté (ce qui est une bonne chose). Mis à part ce petit détail, c'est une classe très utile.

23voto

user2225104 Points 95

Le besoin d'implémentations de délégués C++ est un embarras durable pour la communauté C++. Tous les programmeurs C++ aimeraient les avoir, donc ils finissent par les utiliser malgré les faits :

  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 vers la portabilité ou la conformité aux standards à des degrés plus ou moins importants (veuillez vérifier en inspectant les diverses implémentations de délégués ici et sur codeproject). Je n'ai pas encore vu d'implémentation qui n'utilise pas de reinterpret_casts sauvages, de "prototypes" de classes imbriquées qui, avec un peu de chance, produisent des pointeurs de fonction de la même taille que ceux passés par l'utilisateur, des astuces de compilateur comme d'abord forward declare, puis typedef puis declare encore, cette fois en héritant d'une autre classe ou d'autres techniques louches similaires. Bien qu'il s'agisse d'un grand accomplissement pour les implémenteurs qui ont construit cela, c'est toujours un triste témoignage sur la façon dont le C++ évolue.

  3. Il n'est que rarement souligné que maintenant, après trois révisions de la norme C++, les délégués n'ont pas été correctement traités. (Ou le manque de caractéristiques du langage qui permettent des implémentations de délégués simples).

  4. Avec la façon dont les fonctions lambda C++11 sont définies par la norme (chaque lambda est anonyme, de type différent), la situation ne s'est améliorée que dans certains cas d'utilisation. Mais pour le cas d'utilisation des délégués dans les API de bibliothèque (DLL), les lambdas seul ne sont toujours pas utilisables. La technique courante ici, est d'abord d'emballer le lambda dans une fonction std::et ensuite de le passer à travers l'API.

0 votes

J'ai fait une version des callbacks génériques d'Elbert Mai en C++11, basée sur d'autres idées vues ailleurs, et cela semble résoudre la plupart des problèmes que vous citez en 2). Le seul problème persistant pour moi est que je suis coincé en utilisant une macro dans le code client pour créer les délégués. Il y a probablement un moyen magique de sortir de ce problème que je n'ai pas encore résolu.

5 votes

J'utilise std::function sans heap dans un système embarqué et cela fonctionne toujours.

2 votes

std::function n'a pas toujours allouer de façon dynamique

10voto

SirYakalot Points 1956

Très simplement, un délégué fournit une fonctionnalité pour 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 une certaine méchanceté de gabarit en arrière-plan pour créer une fonction de type pointeur de fonction de classe modèle qui fonctionne de la façon dont vous le souhaitez.

c'est-à-dire que vous pouvez les définir pour qu'ils pointent vers une fonction donnée et vous pouvez les faire circuler et les appeler quand et où vous voulez.

Il y a de très bons exemples ici :

1voto

nmserve Points 1

L'é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 doivent remplir. Il facilite le fonctionnement d'un pointeur de fonction.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X