J'entends toujours parler de reliure précoce et tardive, mais je ne comprends pas ce que c'est. J'ai trouvé l'explication suivante que je ne comprends pas:
La liaison précoce fait référence à l'affectation de valeurs aux variables pendant la conception tandis que la liaison tardive fait référence à l'affectation de valeurs aux variables pendant l'exécution.
Quelqu'un pourrait-il définir les deux types de reliure et les comparer?
Il existe deux principaux concepts de confusion: la liaison et le chargement. Il est confondu par le concept de DataBinding, qui se situe 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 trifecta, l'expédition.
Liaison tardive : le type est inconnu jusqu'à ce que la variable soit exercée pendant l'exécution; généralement par affectation, mais il existe d'autres moyens de contraindre un type; les langages typés dynamiquement appellent cela une fonctionnalité sous-jacente, mais de nombreux langages typés statiquement ont une méthode pour obtenir une liaison tardive
Implémenté souvent à l'aide de types dynamiques [spéciaux], introspection/réflexion, options de drapeaux et de compilateur, ou via des méthodes virtuelles en empruntant et en étendant la répartition dynamique
Liaison précoce : le type est connu avant que la variable ne soit exercée pendant l'exécution, généralement par un moyen déclaratif statique
Implémenté souvent à l'aide de types primitifs standard
Dispatch statique : fonction spécifique connue ou sous-programme au moment de la compilation; il est sans ambiguïté et correspond à la signature
Mis en œuvre en tant que fonctions statiques; aucune méthode ne peut avoir la même signature
Dynamic Dispatch : pas une fonction spécifique ou sous-programme 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", qui se distinguent par les informations contextuelles utilisées pour sélectionner l'implémentation de fonction appropriée.
Dans single [ dynamic] dispatch , seul le type de l'instance est utilisé pour déterminer la mise en œuvre de la fonction appropriée. Dans les langages de type statique, cela signifie en pratique que le type d'instance décide quelle implémentation de méthode est utilisée quel que soit le type de référence indiqué lorsque la variable est déclarée/affectée. Étant donné qu'un seul type - le type de l'instance d'objet - est utilisé pour déduire l'implémentation appropriée, cette approche est appelée "répartition unique".
Il existe également plusieurs [ dynamiques] répartition , où les types de paramètres d'entrée aident également à déterminer lesquels implémentation de la 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 "multiple". envoi".
Mis en œuvre en tant que fonctions virtuelles ou abstraites; d'autres indices incluent les méthodes remplacées, masquées ou masquées.
NB: Que la surcharge de méthode implique ou non la répartition dynamique est spécifique au langage. Par exemple, en Java , les méthodes surchargées sont distribuées statiquement.
Chargement différé : stratégie d'initialisation d'objet qui reporte l'attribution de valeur jusqu'à ce qu'elle soit nécessaire; 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 le charger; souvent trouvé particulièrement utile pour charger de grands ensembles de données ou attendre des ressources externes
Implémenté souvent en ne chargeant pas intentionnellement une collection ou une liste dans un objet composite pendant les appels du constructeur ou de l'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 de méta-informations sur la collection (comme la taille ou les clés), mais en omettant les données réelles; fournit également un mécanisme à certains runtimes pour fournir aux développeurs un schéma d'implémentation singleton assez sûr et efficace
Chargement avéré : stratégie d'initialisation d'objet qui exécute immédiatement toutes les affectations de valeur afin d'avoir toutes les données nécessaires à compléter avant de se considérer comme étant dans un état valide.
Implémenté souvent en fournissant un objet composite avec toutes ses données connues dès que possible, comme lors d'un appel constructeur ou d'une initialisation
Liaison de données : implique souvent la création d'un lien actif ou mappage entre deux flux d'informations compatibles afin que les modifications apportées à l'un soient reflétées dans l'autre et vice-versa l'inverse; pour être compatibles, ils doivent souvent avoir un type de base ou une interface commune
Implémenté souvent comme une tentative de fournir une synchronisation plus propre et cohérente entre les différents aspects de l'application (par exemple, modèle de vue à afficher, modèle à contrôleur, etc.) et parle de concepts comme la source et la cible, les points de terminaison, la liaison/dissociation, la mise à jour et des événements comme on_bind, on_property_change, on_explicit, on_out_of_scope
NOTE DE MODIFICATION: Dernière modification majeure pour fournir une description d'exemples de leur fréquence. Les exemples de code particuliers dépendent entièrement de l'implémentation/du runtime/de la plateforme
Tout ce qui est décidé par le compilateur pendant la compilation peut se référer à EARLY/COMPILE TIME Binding et tout ce qui doit être décidé à RUNTIME est appelé LATE/RUNTIME liaison.
Par exemple,
Méthode surcharge et méthode remplacement.
1) Dans surcharge de méthode vos appels de méthode aux méthodes sont décidés par le compilateur en ce sens que la fonction qui va être appelée est décidée par votre compilateur au moment de la compilation. D'où étant PREMIÈRE LIAISON.
2) Dans la méthode Overriding, il est décidé au RUNTIME quelle méthode va être appelée. Il est donc référencé comme FIN DE LIAISON.
J'ai essayé de le garder simple et facile à obtenir. J'espère que cela t'aides.
La liaison tardive correspond à l'évaluation du comportement lors de l'exécution. C'est nécessaire lorsque vous voulez en effet 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, spécifiquement 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()
appellera en fait void A::f()
, car il est lié tôt (ou statiquement), et donc le programme à l'exécution pense c'est juste un pointeur vers une variable de type A
, tandis que a->g()
appellera en fait void B::g()
, car le compilateur, voyant g()
est virtuel, injecte du code pour rechercher le adresse de la fonction correcte à appeler lors de l'exécution.
si vous connaissez les pointeurs de fonction, ce serait un exemple. On peut dire que les fonctions définies sont une liaison anticipée. tandis que si vous utilisez des pointeurs de fonction, sa liaison tardive.
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 au moment de la compilation de l'éditeur de liens)
mais le pointeur de fonction se lie tardivement, le fp peut appeler add ou sub selon le choix de l'utilisateur [au moment de l'exécution].
La liaison précoce et tardive n'a de sens que dans le contexte des types et non de la façon dont vous la décrivez. Presque toutes les langues modernes sont tapées dans le sens où toutes les valeurs ont des types fixes. La différence vient quand on regarde les langages typiquement dynamiquement vs statiquement. Dans les langages typés dynamiquement, les variables n'ont pas de types, elles peuvent donc faire référence à des valeurs de tout type, ce qui signifie que lorsque vous appelez une méthode sur un objet auquel se réfère une variable, la seule façon de déterminer si cet appel est valide ou non est de recherchez la classe de l'objet et voyez si cette méthode existe réellement. Cela permet des choses intéressantes comme l'ajout de nouvelles méthodes aux classes au moment de l'exécution, car la recherche de méthode réelle est différée jusqu'au tout dernier moment. La plupart des gens appellent cet état de choses contraignant.
Dans un langage statiquement typé, les variables ont des types et une fois déclarées, elles ne peuvent faire référence à aucune valeur qui n'est pas du même type. Ce n'est pas strictement vrai, mais supposons-le pour l'instant. Maintenant, si vous savez que la variable ne fera référence qu'à des valeurs d'un type spécifique, il n'y a aucune raison de déterminer si un appel de méthode est valide ou non au moment de l'exécution, car vous pouvez déterminer la validité avant l'exécution du code. C'est ce qu'on appelle la liaison anticipée.
Un exemple pour démontrer la liaison tardive dans Ruby:
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 comme Java où tous les types sont fixes au moment de l'exécution.
Au lieu de vous donner une définition académique, je vais essayer de vous montrer certaines des différences en utilisant un exemple du monde réel en utilisant VBA:
liaison 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 sur le composant "Microsoft Scripting Runtime" à design time. Il a l'avantage que vous obtenez un message d'erreur déjà au moment de la compilation lorsque vous avez une faute de frappe dans FileSystemObject
ou des noms de méthode comme GetSpecialFolder
.
liaison tardive
Dim x As Object
Set x = CreateObject("Scripting.FileSystemObject")
Debug.Print x.GetSpecialFolder(0)
Cela ne nécessite pas de définition préalable d'une référence, la création d'instance et la détermination du type se feront uniquement 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 inexistante de x
, cela entraînera une erreur d'exécution uniquement lorsque la ligne spécifique sera exécutée.
Ainsi, l'inconvénient de la liaison tardive est que vous n'avez aucun contrôle de type fort ici. Mais c'est aussi l'avantage - disons que vous avez un composant où plusieurs versions existent, et chaque version plus récente fournit des fonctions supplémentaires. (Un exemple réel est les composants MS Office, comme l'interface Excel COM) La liaison tardive vous permet d'écrire du code qui fonctionne avec toutes ces versions - vous pouvez d'abord déterminer la version spécifique du composant, et si vous découvrez que vous avez seule une version plus ancienne est disponible, évitez d'exécuter des appels de fonctions qui ne fonctionnent pas avec cette version.