Quelle est la différence entre Dynamic dispatch et late binding en C++?

j'ai récemment lu à propos de la dynamic Dispatch sur Wikipedia et je ne pouvais pas comprendre la différence entre la dynamic dispatch et la late binding en C++.

lorsque chacun des mécanismes est utilisé?

la citation exacte de Wikipedia:

L'expédition dynamique diffère de la liaison tardive (également appelée liaison dynamique). Dans le contexte de la sélection d'une opération, le terme contraignant renvoie le processus d'associer un nom à une opération. Dispatching se réfère au choix d'une implémentation pour l'opération après que vous avez décidé à quelle opération un nom se réfère. Avec Dynamic dispatch, le nom peut être lié à une opération polymorphique au moment de la compilation, mais l'implémentation ne sera pas choisie avant l'exécution (c'est ainsi que dynamic dispatch fonctionne en C++). Cependant, la liaison tardive implique une expédition dynamique, puisque vous ne pouvez pas choisir quelle implémentation d'une opération polymorphique pour sélectionnez jusqu'à ce que vous avez sélectionné l'opération que le nom se réfère.

65
demandé sur Andre Silva 2013-11-25 12:15:52

11 réponses

une réponse assez correcte à cette question Est en fait incorporée dans une question sur la fixation tardive ou anticipée de programmers.stackexchange.com .

en bref, la liaison tardive fait référence à objet - côté d'une expédition eval, la régulation dynamique fait référence au côté fonctionnel. En fin de liaison le type d'une variable est la variante à l'exécution. Dans dynamic-dispatch, la fonction ou sous-programme exécuté est variante.

En C++, nous n'avons pas vraiment la liaison tardive, car le type est connu (pas nécessairement la fin de la hiérarchie d'héritage, mais au moins une base formelle de la classe ou de l'interface). Mais nous do avons l'expédition dynamique via les méthodes virtuelles et le polymorphisme.

le meilleur exemple que je puisse offrir pour la reliure tardive est l '"objet" non typé dans Visual Basic. L'environnement d'exécution n'tous les liaison tardive levage lourd pour vous.

Dim obj

- initialize object then..
obj.DoSomething()

le compilateur va en fait coder le contexte d'exécution approprié pour le runtime-engine pour effectuer une recherche nommée de la méthode appelée DoSomething , et si elle est découverte avec les paramètres correspondant correctement, exécutez en fait l'appel sous-jacent. En réalité, quelque chose sur le type de l'objet est connu (il hérite de IDispatch et soutient GetIDsOfNames() , etc.). mais aussi loin que l' langue concerne le type de la variable est totalement inconnu au moment de la compilation, et il n'a aucune idée si DoSomething est même une méthode pour ce que obj réellement est jusqu'à ce que l'exécution atteint le point d'exécution.

Je ne vais pas prendre la peine de jeter une interface virtuelle C++ et'al, car je suis sûr que vous savez déjà à quoi ils ressemblent. J'espère qu'il est évident que le langage C++ ne peut tout simplement pas faire ce. Elle est fortement typée. Il peut (et fait, évidemment) l'expédition dynamique via la caractéristique de méthode virtuelle polymorphique.

56
répondu WhozCraig 2017-04-12 07:31:17

Late binding appelle une méthode par son nom pendant l'exécution. Vous ne l'avez pas vraiment en c++, sauf pour importer des méthodes à partir d'une DLL.

Un exemple pour cela serait: Getprocadress ()

avec Dynamic dispatch, le compilateur dispose de suffisamment d'informations pour appeler la bonne mise en œuvre de la méthode. Ceci est généralement fait en créant une table virtuelle .

8
répondu Yochai Timmer 2013-11-25 08:32:31

le link expliquait lui-même la différence:

L'expédition dynamique diffère de la liaison tardive (également appelée liaison dynamique). Dans le contexte de la sélection d'une opération, la liaison se réfère au processus d'associer un nom à une opération. Dispatching se réfère au choix d'une implémentation pour l'opération après que vous avez décidé à quelle opération un nom se réfère.

et

avec Dynamic dispatch, le nom peut être lié à une opération polymorphique au moment de la compilation, mais l'implémentation ne doit pas être choisie avant l'exécution (c'est ainsi que dynamic dispatch fonctionne en C++). Cependant, la reliure tardive implique une expédition dynamique puisque vous ne pouvez pas choisir l'implémentation d'une opération polymorphique à sélectionner tant que vous n'avez pas sélectionné l'opération à laquelle le nom se réfère.

mais ils sont presque égaux en C++ vous pouvez faire une expédition dynamique par fonctions virtuelles et vtables.

c++ utilise la liaison précoce et offre une régulation dynamique et statique. La forme d'expédition par défaut est statique. Pour obtenir la distribution dynamique, vous devez déclarer une méthode virtuelle.

8
répondu deepmax 2013-11-25 08:41:58

en C++, les deux sont identiques.

en C++, il y a deux types de reliure:

  • statique de la liaison qui se fait au moment de la compilation.
  • dynamique de la liaison qui se fait au moment de l'exécution.
  • " late binding et la liaison statique est parfois appelée early binding .

    à l'aide des fonctions" virtual (ou function pointers ), et à l'aide de la fonction "static-binding", tous les appels de fonctions autres sont résolus.

5
répondu Nawaz 2013-11-25 08:30:54

Liaison désigne le processus d'associer un nom à une opération.

la principale chose ici est les paramètres de fonction qui décident quelle fonction appeler à l'exécution

Dispatching se réfère au choix d'une implémentation pour l'opération après que vous ayez décidé à quelle opération un nom se réfère.

contrôle d'expédition à celui selon la correspondance des paramètres

http://en.wikipedia.org/wiki/Dynamic_dispatch

espère que cela vous aider à

5
répondu bhupinder 2013-11-25 08:36:07

étant donné que la définition wordy Wikipedia je serais tenté de classer l'expédition dynamique comme la liaison tardive de C++

struct Base {
    virtual void foo(); // Dynamic dispatch according to Wikipedia definition
    void bar();         // Static dispatch according to Wikipedia definition
};

reliure tardive à la place, pour Wikipedia, semble signifier l'envoi d'un pointeur vers un membre de c++

(this->*mptr)();

où la sélection de ce qui est l'opération invoquée (et pas seulement quelle implémentation) est faite à l'exécution.

dans la littérature c++ cependant late binding est normalement utilisé pour ce que Wikipédia appels répartition dynamique.

2
répondu 6502 2013-11-25 08:31:46

Permettez-moi de vous donner un exemple des différences parce qu'elles ne sont pas les mêmes. Oui, dynamic dispatch vous permet de choisir la bonne méthode Lorsque vous faites référence à un objet par une superclasse, mais cette magie est très spécifique à cette hiérarchie de classe, et vous devez faire quelques déclarations dans la classe de base pour la faire fonctionner (les méthodes abstraites remplissent les vtables puisque l'index de la méthode dans la table ne peut pas changer entre des types spécifiques). Donc, vous pouvez appeler méthodes dans Tabby et Lion et Tigre tout par un pointeur de chat générique et ont même des tableaux de chats remplis de Lions et tigres et Tabbys. Il sait ce que indexes ces méthodes se réfèrent dans le vtable de l'objet à la compilation (static/early binding), même si la méthode est sélectionnée à l'exécution (dynamic dispatch).

maintenant, mettons en place un réseau qui contient des Lions, des tigres et des Ours! ((Oh My!)). En supposant que nous n'avons pas une classe de base appelée Animal, C++, vous allez avoir beaucoup de travail à faire parce que le compilateur ne va pas vous permettre de faire toute la distribution dynamique sans une classe de base commune. Les index des vtables doivent correspondre, et cela ne peut pas être fait entre des classes non-publiées. Vous devez avoir un vtable assez grand pour contenir les méthodes virtuelles de toutes les classes dans le système. Les programmeurs C++ voient rarement cela comme une limitation parce que vous avez été formé pour penser d'une certaine manière sur la conception de classe. Je ne dis pas sa meilleure ou mauvais.

avec reliure tardive, le run-time s'occupe de cela sans classe de base commune. Il y a normalement une table de hachage utilisé pour trouver des méthodes dans les classes avec un système de cache utilisé dans le répartiteur. Où en C++, le compilateur connaît tous les types. Dans une langue tardive, les objets eux-mêmes connaissent leur type (ce n'est pas sans caractère, les objets eux-mêmes savent exactement qui ils sont dans la plupart des cas). Cela signifie que je peux avoir des tableaux de plusieurs types d'objets si je souhaitez (les Lions et les Tigres et des Ours). Et vous pouvez implémenter le transfert de messages et le prototypage (permet de modifier les comportements par objet sans changer la classe) et toutes sortes d'autres choses de manière beaucoup plus flexible et conduisant à moins de code que dans les langues qui ne supportent pas la reliure tardive.

avez-vous déjà programmé sur Android et utilisé findViewById()? Vous finissez presque toujours par mouler le résultat pour obtenir le bon type, et le moulage est essentiellement mentir à la compiler et renoncer à toute la qualité statique de vérification de type qui est censé rendre les langues statiques supérieur. Bien sûr, vous pourriez avoir à la place findTextViewById (), findEditTextById (), et un million d'autres de sorte que vos types de retour correspondent, mais qui jette polymorphisme par la fenêtre; sans doute la base entière de OOP. Une langue liée tardivement vous laisserait probablement simplement indexer par un ID, et le traiter comme une table de hachage et ne pas se soucier de ce que le type était indexé ou retourné.

voici un autre exemple. Disons que vous avez votre Lion classe et de son comportement par défaut est de vous manger quand vous le voyez. En C++, si vous voulez avoir un seul lion "entraîné", vous devez créer une nouvelle sous-classe. Le prototypage vous permettrait simplement de changer une ou deux méthodes de ce Lion particulier qui doivent être changées. La classe et le type ne changent pas. C++ ne peut pas le faire. C'est important car quand vous avez un nouveau "AfricanSpottedLion" qui hérite de Lion, vous pouvez l'entraîner trop. Le prototypage ne modifie pas la structure de la classe et peut donc être étendu. C'est normalement la façon dont ces langues gèrent les problèmes qui nécessitent normalement un héritage multiple, ou peut-être un héritage multiple est la façon dont vous gérez un manque de prototypage.

POUR INFO, objectif-C est C avec le message de SmallTalk passé ajouté et SmallTalk est l'OOP d'origine, et les deux sont en retard lié avec toutes les fonctionnalités ci-dessus et plus. Les langues tardives peuvent être légèrement plus lentes à partir d'un micro-niveau le code peut souvent être structuré d'une manière plus efficace à un macro-niveau, et tout se résume à la préférence.

2
répondu Evan Langlois 2015-07-18 16:33:22

Dynamique de l'expédition est ce qui se passe lorsque vous utilisez le virtual clé en C++. Ainsi par exemple:

struct Base
{
    virtual int method1() { return 1; }
    virtual int method2() { return 2; } // not overridden
};

struct Derived : public Base
{
    virtual int method1() { return 3; }
}

int main()
{
    Base* b = new Derived;
    std::cout << b->method1() << std::endl;
}

affichera 3 , parce que la méthode a été expédié dynamiquement . Le standard C++ est très prudent et non pour spécifier comment cela se produit exactement en coulisse, mais chaque compilateur sous le Soleil le fait de la même manière. Ils créent un tableau de pointeurs de fonction pour chaque type polymorphe (appelé le "151970920 table virtuelle ou vtable ), et lorsque vous appelez une méthode virtuelle, la "vraie" méthode est recherchée dans la vtable, et cette version est appelée. Vous pouvez donc imaginer quelque chose comme ce pseudo:

struct BaseVTable
{
    int (*_method1) () = &Base::method1; // real function address
    int (*_method2) () = &Base::method2;
};

struct DerivedVTable
{  
    int (*method) () = &Derived::method1;
    int (*method2) () = &Base::method2; // not overridden
};

de cette façon, le compilateur peut être sûr qu'une méthode avec une signature particulière existe au moment de la compilation. Cependant, à l'exécution, l'appel peut en fait être envoyé via le vtable vers une fonction différente. Les appels vers des fonctions virtuelles sont un tout petit peu plus lents que les appels non-virtuels, en raison de l'étape supplémentaire d'indirection.


d'un autre côté, mon interprétation du terme reliure tardive est que le pointeur de fonction est regardé par nom à l'exécution, à partir d'une table de hachage ou quelque chose de similaire. C'est la façon dont les choses sont faites en Python, JavaScript et (si la mémoire est utile) Objective-C. Cela permet de ajouter de nouvelles méthodes à une classe à l'exécution , ce qui ne peut pas être fait directement en C++. Cela est particulièrement utile pour mettre en œuvre des choses comme les mixins. Cependant, l'inconvénient est que la recherche de temps d'exécution est généralement beaucoup plus lente que même un appel virtuel en C++, et le compilateur n'est pas en mesure d'effectuer une vérification de type de compilation pour les méthodes nouvellement ajoutées.

1
répondu Tristan Brindle 2013-11-25 08:36:52

je suppose que le sens est quand vous avez deux classes B, C hérite de la même classe de père A. Donc, pointeur du Père (type A) peut tenir chacun des types de fils. Le compilateur ne peut pas savoir ce que le type tient dans le pointeur dans un certain temps, parce qu'il peut changer pendant l'exécution du programme.

il y a des fonctions spéciales pour déterminer quel type d'objet dans un certain temps. comme instanceof en java, ou par if(typeid(b) == typeid(A))... en c++.

0
répondu MeNa 2013-11-25 08:33:02

Ce question peut vous aider.

"Dynamic dispatch" désigne généralement une expédition multiple.

Considérons l'exemple ci-dessous. J'espère que ça peut vous aider.

    class Base2;
    class Derived2; //Derived2 class is child of Base2
class Base1 {
    public:
        virtual void function1 (Base2 *);
        virtual void function1 (Derived2 *);
}

class Derived1: public Base1 {
    public:
    //override.
    virtual void function1(Base2 *);
    virtual void function1(Derived2 *);
};

examiner le cas ci-dessous.

Derived1 * d = new Derived1;
Base2 * b = new Derived2;

//Now which function1 will be called.
d->function1(b);

il appellera function1 prendre Base2* pas Derived2* . Cela est dû à l'absence de régulation dynamique multiple.

Late la liaison est l'un des mécanismes de mise en œuvre de l'expédition dynamique unique.

0
répondu doptimusprime 2017-05-23 12:32:24

En C++, les deux dynamic dispatch et late binding est le même. Fondamentalement, la valeur d'un seul objet détermine le morceau de code invoqué à l'exécution. Dans les langages comme C++ et java, dynamic dispatch est plus spécifiquement dynamic single dispatch qui fonctionne comme indiqué ci-dessus. Dans ce cas, puisque la liaison se produit à l'exécution, elle est aussi appelée late binding . Les langages comme smalltalk permettent l'expédition dynamique multiple dans laquelle la méthode runtime est choisie à l'exécution basée sur le identités ou valeurs de plus d'un objet.

en C++ nous n'avons pas vraiment de reliure tardive, parce que l'information de type est connue. Ainsi, dans le contexte C++ ou Java, Dynamic dispatch et late binding sont les mêmes. Reliure actuelle / tardive, je pense que c'est dans les langages comme python qui est une recherche basée sur la méthode plutôt que sur le type.

0
répondu jester 2013-11-25 08:36:19