Qu'est-ce qu'une liaison précoce et tardive?

77

Je continue à entendre parler de liaison précoce et tardive, mais je ne comprends pas ce qu’elles sont. J'ai trouvé l'explication suivante que je ne comprends pas:

La liaison précoce fait référence à l'attribution de valeurs aux variables pendant la conception, tandis que la liaison tardive correspond à l'attribution de valeurs aux variables pendant l'exécution.

Quelqu'un pourrait-il définir les deux types de reliure et les comparer?

SAMIR RATHOD
la source
1
compile time vs runtime.
barlop
Voici une bonne lecture sur le sujet: en.wikibooks.org/wiki/Introduction_to_Programming_Languages/…
Jay Elston

Réponses:

84

Il y a deux concepts principaux dans la confusion: la liaison et le chargement. Il est confondu avec le concept de DataBinding, qui est quelque part au milieu et fait souvent les deux. Après y avoir réfléchi, je vais ajouter un autre concept, pour compléter le triplé, dépêche.

Les types

Liaison tardive : le type est inconnu jusqu'à ce que la variable soit exercée pendant l'exécution; généralement par cession, mais il existe d'autres moyens de contraindre un type; les langages à typage dynamique appellent cela une fonctionnalité sous-jacente, mais de nombreux langages à typage statique ont une méthode permettant d'obtenir une liaison tardive

Mis en œuvre souvent à l'aide de types dynamiques [spéciaux], d'introspection / réflexion, d'indicateurs et d'options de compilation, ou via des méthodes virtuelles en empruntant et en étendant la répartition dynamique

Early Binding : le type est connu avant que la variable ne soit exercée pendant l'exécution, généralement par l'intermédiaire d'un moyen déclaratif statique

Implémenté souvent à l'aide de types primitifs standard

Les fonctions

Répartition statique : fonction ou sous-programme spécifique connu au moment de la compilation; il est sans ambiguïté et assorti de la signature

Implémenté en tant que fonctions statiques; aucune méthode ne peut avoir la même signature

Dispatch dynamique : pas une fonction ou un sous-programme spécifique au moment de la compilation; déterminé par le contexte lors de l'exécution. Il existe deux approches différentes de la "répartition dynamique", différenciées par les informations contextuelles utilisées pour sélectionner l'implémentation de la fonction appropriée.

Dans la répartition unique [ dynamique ] , seul le type de l'instance est utilisé pour déterminer l'implémentation de la fonction appropriée. Dans les langages à types statiques, cela signifie en pratique que le type d'instance décide quelle implémentation de la méthode est utilisée, quel que soit le type de référence indiqué lors de la déclaration / affectation de la variable. Étant donné que seul un type, le type de l'instance d'objet, est utilisé pour déduire l'implémentation appropriée, cette approche est appelée "envoi unique".

Il existe également de multiples répartitions [ dynamiques ] , où les types de paramètres d'entrée aident également à déterminer quelle implémentation de fonction appeler. Étant donné que plusieurs types, à la fois le type de l'instance et le ou les types de paramètre (s), influencent la mise en œuvre de la méthode sélectionnée, cette approche est appelée "envoi multiple".

Implémenté en tant que fonctions virtuelles ou abstraites; d'autres indices incluent les méthodes écrasées, cachées ou ombrées.

NB: Que la surcharge de méthode implique une dépêche dynamique dépend de la langue. Par exemple, en Java, les méthodes surchargées sont distribuées de manière statique.

Valeurs

Chargement paresseux : stratégie d'initialisation d'objet qui reporte l'attribution de valeur jusqu'au besoin ; permet à un objet d'être dans un état essentiellement valide mais sciemment incomplet et d'attendre que les données soient nécessaires avant de les charger; souvent trouvé particulièrement utile pour le chargement de grands ensembles de données ou l'attente de ressources externes

Implémenté souvent en ne chargeant pas délibérément une collection ou une liste dans un objet composite pendant les appels de constructeur ou d'initialisation jusqu'à ce qu'un appelant en aval demande à voir le contenu de cette collection (par exemple, get_value_at, get_all_as, etc.). Les variations incluent le chargement d'informations méta sur la collection (telles que la taille ou les clés), mais en omettant les données réelles; fournit également un mécanisme permettant à certaines exécutions de fournir aux développeurs un schéma d'implémentation singleton assez sûr et efficace

Eager Loading : stratégie d'initialisation d'objet qui exécute immédiatement toutes les attributions de valeur afin que toutes les données nécessaires soient complètes avant de se considérer comme étant dans un état valide.

Souvent mis en œuvre en fournissant à un objet composite toutes les données connues le plus rapidement possible, comme lors d'un appel ou d'une initialisation du constructeur

Liaison de données : implique souvent la création d'un lien actif ou d'une carte entre deux flux d'informations compatibles, de sorte que les modifications apportées à l'un soient reflétées dans l'autre, et inversement; afin d'être compatibles, ils doivent souvent avoir un type de base commun, ou une interface

Souvent implémentée dans le but de fournir une synchronisation plus propre et cohérente entre différents aspects de l’application (par exemple, le modèle de vue à afficher, le modèle sur le contrôleur, etc.), elle aborde des concepts tels que source et cible, points de terminaison, liaison / dissociation, mise à jour et événements tels que on_bind, on_property_change, on_explicit, on_out_of_scope


EDIT NOTE: Dernière édition majeure pour fournir une description d’exemples de la façon dont elles se produisent souvent. Les exemples de code particuliers dépendent entièrement de la mise en œuvre / exécution / plate-forme

JustinC
la source
2
Cette réponse semble trop spécifique aux langages orientés objet.
Jack
27

Tout ce qui est décidé par le compilateur pendant la compilation peut être référencé dans la liaison EARLY / COMPILE TIME et tout ce qui doit être décidé à RUNTIME est appelé liaison LATE / RUNTIME .

Par exemple,

Méthode surcharge et méthode Substitution .

1) Dans la méthode Surcharge de votre méthode, les appels aux méthodes sont décidés par le compilateur en ce sens que la fonction à appeler est déterminée par votre compilateur au moment de la compilation. D'où être tôt liaison .

2) En dérogation de méthode, il est décidé à RUNTIME quelle méthode sera appelée. Donc, il est fait référence à LATE BINDING .

J'ai essayé de rester simple et facile à obtenir. J'espère que cela t'aides.

Trapaank
la source
9

La liaison tardive se produit lorsque le comportement est évalué au moment de l'exécution. Il est nécessaire lorsque vous voulez vraiment déterminer comment agir en fonction des informations dont vous disposez uniquement lorsque le programme est en cours d'exécution. L'exemple le plus clair à mon avis est le mécanisme de la fonction virtuelle, en particulier en C ++.

class A
{
public:
    void f() {}
    virtual void g() {}
};

class B : public A
{
    void f() {}
    virtual void g() {}
};

int main()
{
    A* a = new B;
    a->f();
    a->g();
}

Dans cet exemple, a->f()appelera en fait void A::f(), car il est lié tôt (ou statiquement), et le programme au moment de l'exécution pense qu'il s'agit simplement d'un pointeur sur une Avariable de type, alors a->g()qu'il appellera en fait void B::g(), car le compilateur, voyant g()est virtuel, injecte du code pour rechercher l'adresse de la fonction correcte à appeler au moment de l'exécution.

Yam Marcovic
la source
1
"Le runtime"? Vous parlez de C ++. C ++ compile directement en code machine, il n'a pas besoin d'un runtime pour résoudre les méthodes virtuelles.
tdammers
3
@tdammers C ++ a en fait besoin d'une bibliothèque d'exécution, mais pas pour les appels virtuels. Si vous lisez attentivement, vous remarquerez que cette réponse indique que le compilateur "injecte du code pour rechercher l'adresse de la fonction correcte [...] au moment de l' exécution".
Bien, mais ce "code pour rechercher l’adresse de la fonction correcte" est fondamentalement juste un déréférencement de pointeur en deux étapes, indépendant du type, suivi d’un appel de fonction. Il n'y a pas de "pensée" impliqué; la seule raison pour laquelle cela fonctionne de manière fiable est que le compilateur vérifie le type au moment de la compilation ; au moment de l'exécution, le code généré confie au compilateur le travail de vérification de type. Si vous utilisez des transtypages non sécurisés (par exemple , des transtors pointeurs de style C), vous pouvez légalement traiter les objets C ++ en tant qu'objet de la mauvaise classe, mais leurs tables vtables seront complètement foirées et le code sera simplement cassé.
tdammers
@tdammers J'ai essayé de rester à l'écart de ce type de réponse, car il s'agit d'un détail d'implémentation des compilateurs, ce qui peut être vrai ou non pour certains compilateurs ésotériques. Ce qui compte, c'est le concept.
Yam Marcovic
1
@tdammers Et par "le runtime", je veux dire "le programme à l'exécution". De toute évidence, C ++ n'est pas géré. Mais puisque vous m'avez montré que cela pouvait semer la confusion, je le remplace par le libellé complet.
Yam Marcovic
5

si vous êtes familier avec les pointeurs de fonction, ce serait un exemple. Les fonctions définies peuvent être considérées comme une liaison anticipée. alors que si vous utilisez des pointeurs de fonction, sa dernière liaison.

  int add(int x,int y)
  {
    return x+y;
  }
  int sub(int x,int y)
  {
      return x-y;
  }


    int main()
    {
     //get user choice
     int(*fp)(int,int);
     //if add
      fp=add;
     //else if sub
     fp=sub;
     cout<<fp(2,2);
    }

ici les fonctions add et sub sont des fonctions (son adresse est liée lors de la compilation du link-time)

mais le pointeur de la fonction lie tardivement, le fp peut appeler add ou sub en fonction du choix de l'utilisateur [au moment de l'exécution].

Dineshkumar
la source
3

La liaison précoce et tardive n’a de sens que dans le contexte des types et non dans la manière dont vous la décrivez. Presque toutes les langues modernes sont dactylographiées dans le sens où toutes les valeurs ont des types fixes. La différence intervient lorsque nous examinons des langages dynamiquement vs statiquement typés. Dans les langages à typage dynamique, les variables n'ont pas de types et peuvent donc faire référence à des valeurs de n'importe quel type. Cela signifie que lorsque vous appelez une méthode sur un objet référencé par une variable, le seul moyen de déterminer si cet appel est valide ou non est de: recherchez la classe pour l'objet et voyez si cette méthode existe réellement. Cela permet certaines choses intéressantes, telles que l'ajout de nouvelles méthodes aux classes au moment de l'exécution, car la recherche de la méthode réelle est différée jusqu'au dernier moment. La plupart des gens appellent cet état de fait une liaison tardive.

Dans un langage de type statique, les variables ont des types et, une fois déclarées, ne peuvent faire référence à aucune valeur qui ne soit pas du même type. Ce n'est pas strictement vrai, mais supposons-le pour le moment. Maintenant, si vous savez que la variable ne fera jamais référence qu'à des valeurs d'un type spécifique, il n'y a aucune raison de savoir si un appel de méthode est valide ou non au moment de l'exécution, car vous pouvez déterminer la validité avant que le code ne soit exécuté. Ceci est appelé liaison anticipée.

Un exemple pour démontrer une liaison tardive en rubis:

a = 1 # a is an integer at this point
a.succ # asking for its successor is valid

class A
  def method_a
    # some code
  end
end

a = A.new
a.method_a # this is also valid
a.succ # this is not valid


class A # we can re-open the class and add a method
  def succ
    # some more code
  end
end
a.succ # now this is valid

La séquence d'actions ci-dessus n'est pas possible dans un langage tel que Java où tous les types sont fixés au moment de l'exécution.

davidk01
la source
1

Au lieu de vous donner une définition théorique, je vais essayer de vous montrer certaines des différences en utilisant un exemple concret utilisant VBA:

Reliure anticipée:

Dim x As FileSystemObject
Set x = New FileSystemObject
Debug.Print x.GetSpecialFolder(0)

Cela nécessite qu'une référence soit définie au composant "Microsoft Scripting Runtime" au moment de la conception . Cela a l’avantage que vous obtenez déjà un message d’erreur au moment de la compilation lorsque vous avez une faute de frappe FileSystemObjectou des noms de méthodes tels que GetSpecialFolder.

Liaison tardive

Dim x As Object
Set x = CreateObject("Scripting.FileSystemObject")
Debug.Print x.GetSpecialFolder(0)

Cela n'exige pas qu'une référence soit définie à l'avance, la création de l'instance et la détermination du type auront simplement lieu au moment de l'exécution. Le compilateur ne se plaindra pas au moment de la compilation lorsque vous essayez d'appeler une méthode non existante x, cela entraînera une erreur d'exécution uniquement lorsque la ligne spécifique est exécutée.

Ainsi, l’inconvénient de la liaison tardive est que vous n’avez aucune vérification de type forte ici. Mais c’est aussi l’avantage: supposons que vous disposiez d’un composant comportant plusieurs versions et que chaque nouvelle version fournisse des fonctions supplémentaires. (Un exemple concret concerne les composants MS Office, tels que l'interface COM Excel). Une liaison tardive vous permet d'écrire du code qui fonctionne avec toutes ces versions. Vous pouvez d'abord déterminer la version du composant spécifique. Si vous découvrez que vous avez seulement une version plus ancienne disponible, évitez d'exécuter des appels de fonctions qui ne fonctionnent pas avec cette version.

Doc Brown
la source
-2

L’exemple le plus courant de liaison tardive est peut-être la résolution des URL Internet. Il prend en charge les systèmes dynamiques et les grands systèmes sans essayer de lier et de relier tous les sites du monde avant de pouvoir en joindre un, mais il entraîne en revanche un surcoût (recherche DNS, beaucoup moins de routage IP) lors de l'exécution.

De ce fait, la plupart des types de liaisons dans les environnements de langage sont plus ou moins précoces, au moment de la compilation ou de la liaison.

Chaque type a des coûts et des avantages.

JRStern
la source
Pouvez-vous définir une référence pour cette définition de liaison? Je n'ai pas entendu parler de résolution des adresses Internet en tant que "liaison", bien que, comme liaison soit l'acte de résolution de noms, je suppose que quelqu'un a fait valoir que le concept de liaison précoce / tardive peut être appliqué à la résolution d'URI d'adresses Internet. Mais ce n’est pas une interprétation courante et le concept de liaison précoce / tardive est antérieur à l’époque où les ordinateurs étaient généralement connectés à Internet.
Jay Elston